de.innovationgate.wgpublisher.webtml.Base.java Source code

Java tutorial

Introduction

Here is the source code for de.innovationgate.wgpublisher.webtml.Base.java

Source

/*******************************************************************************
 * Copyright 2009, 2010 Innovation Gate GmbH. All Rights Reserved.
 * 
 * This file is part of the OpenWGA server platform.
 * 
 * OpenWGA is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * In addition, a special exception is granted by the copyright holders
 * of OpenWGA called "OpenWGA plugin exception". You should have received
 * a copy of this exception along with OpenWGA in file COPYING.
 * If not, see <http://www.openwga.com/gpl-plugin-exception>.
 * 
 * OpenWGA is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with OpenWGA in file COPYING.
 * If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package de.innovationgate.wgpublisher.webtml;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTag;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.servlet.jsp.tagext.DynamicAttributes;
import javax.servlet.jsp.tagext.Tag;

import org.apache.log4j.Logger;
import org.dom4j.Element;

import de.innovationgate.utils.ReplaceProcessor;
import de.innovationgate.utils.URLBuilder;
import de.innovationgate.utils.WGUtils;
import de.innovationgate.webgate.api.WGAPIException;
import de.innovationgate.webgate.api.WGContent;
import de.innovationgate.webgate.api.WGDatabase;
import de.innovationgate.webgate.api.WGDocument;
import de.innovationgate.webgate.api.WGException;
import de.innovationgate.webgate.api.WGExpressionException;
import de.innovationgate.webgate.api.WGIllegalArgumentException;
import de.innovationgate.wga.model.BrowsingSecurity;
import de.innovationgate.wga.server.api.TMLScript;
import de.innovationgate.wga.server.api.WGA;
import de.innovationgate.wgpublisher.DeployedLayout;
import de.innovationgate.wgpublisher.WGACore;
import de.innovationgate.wgpublisher.WGAVersion;
import de.innovationgate.wgpublisher.WGPDispatcher;
import de.innovationgate.wgpublisher.expressions.ExpressionEngine;
import de.innovationgate.wgpublisher.expressions.ExpressionEngineFactory;
import de.innovationgate.wgpublisher.expressions.ExpressionResult;
import de.innovationgate.wgpublisher.websockets.PageConnection;
import de.innovationgate.wgpublisher.websockets.TMLPageWebSocket;
import de.innovationgate.wgpublisher.webtml.Base.DynamicAttribute;
import de.innovationgate.wgpublisher.webtml.actions.TMLAction;
import de.innovationgate.wgpublisher.webtml.actions.TMLActionCallParameters;
import de.innovationgate.wgpublisher.webtml.actions.TMLActionLink;
import de.innovationgate.wgpublisher.webtml.portlet.TMLPortlet;
import de.innovationgate.wgpublisher.webtml.utils.AjaxActionDefinition;
import de.innovationgate.wgpublisher.webtml.utils.BooleanItemExpression;
import de.innovationgate.wgpublisher.webtml.utils.HTMLHeadInclusion;
import de.innovationgate.wgpublisher.webtml.utils.HttpErrorException;
import de.innovationgate.wgpublisher.webtml.utils.RootTagReceptor;
import de.innovationgate.wgpublisher.webtml.utils.TMLContext;
import de.innovationgate.wgpublisher.webtml.utils.TMLDesignContext;
import de.innovationgate.wgpublisher.webtml.utils.TMLException;
import de.innovationgate.wgpublisher.webtml.utils.TMLOption;
import de.innovationgate.wgpublisher.webtml.utils.TMLOptionPreserver;
import de.innovationgate.wgpublisher.webtml.utils.TMLPageImpl;
import de.innovationgate.wgpublisher.webtml.utils.TMLSilentCancelException;
import de.innovationgate.wgpublisher.webtml.utils.TMLUserProfile;
import de.innovationgate.wgpublisher.webtml.utils.VoidAttribute;

public abstract class Base extends BodyTagSupport implements DynamicAttributes {

    private static final long serialVersionUID = 1L;

    public enum DynamicAttributeValueType {
        STRING, ITEM_EXPRESSION
    }

    public class DynamicAttribute {

        private String _fullName;

        public DynamicAttribute(String fullName, String prefix, String baseName, String value,
                DynamicAttributeValueType valueType) {
            _fullName = fullName;
            _prefix = prefix;
            _baseName = baseName;
            _value = value;
            _valueType = valueType;
        }

        private String _prefix;
        private String _baseName;
        private String _value;
        private DynamicAttributeValueType _valueType;

        public String getPrefix() {
            return _prefix;
        }

        public String getBaseName() {
            return _baseName;
        }

        public String getValue() {
            return Base.this.getTagAttributeValue(_fullName, _value, null);
        }

        public DynamicAttributeValueType getValueType() {
            return _valueType;
        }

        public Object getDynamicValue(TMLContext tmlContext) throws WGException {

            String resolvedValue = getValue();

            if (_valueType == DynamicAttributeValueType.ITEM_EXPRESSION) {
                if (resolvedValue.equals(resolvedValue.toUpperCase()))
                    return tmlContext.meta(resolvedValue);
                else
                    return tmlContext.item(resolvedValue);
            } else {
                return resolvedValue;
            }
        }

        public String getFullName() {
            return _fullName;
        }

    }

    public class MixedAttributeResolver implements ReplaceProcessor {

        public int replace(String text, int from, int to, Writer out) throws IOException {

            // find end position
            int endPos = text.indexOf("}", from);
            if (endPos == -1) {
                out.write("{");
                return to + 1;
            }

            // Cut out dynamic part
            String dynamicPart = text.substring(from, endPos + 1);

            // Resolve
            out.write(Base.this.resolveDynamicAttribute(dynamicPart, null));

            // Continue after dynamic part
            return endPos + 1;

        }

    }

    public interface ResultInterceptor {

        public boolean interceptResult(BaseTagStatus tagStatus);

    }

    protected static final Logger log = Logger.getLogger("wga");

    public static final DateFormat DEBUG_TIMESTAMP_FORMAT = new SimpleDateFormat("HH:mm:ss SSSS");

    public static final String SYSTEMOPTION_PREFIX = "$$wga_";

    public static final String OPTION_DEFAULT_XPLANGUAGE = SYSTEMOPTION_PREFIX + "defaultxpl";
    public static final String OPTION_DESIGNDB = SYSTEMOPTION_PREFIX + "designdb";
    public static final String OPTION_LINK_ACTION = SYSTEMOPTION_PREFIX + "linkAction";
    public static final String OPTION_LINK_MEDIUM = SYSTEMOPTION_PREFIX + "linkMedium";
    public static final String OPTION_PORTLET_NAMESPACE = SYSTEMOPTION_PREFIX + "portletKey";
    public static final String OPTION_CURRENT_MEDIAKEY = SYSTEMOPTION_PREFIX + "currentMediaKey";
    public static final String OPTION_TMLMODULE_NAME = SYSTEMOPTION_PREFIX + "tmlmoduleName";
    public static final String OPTION_TMLMODULE_MEDIAKEY = SYSTEMOPTION_PREFIX + "tmlmoduleMediakey";
    public static final String OPTION_RESULT_INTERCEPTORS = SYSTEMOPTION_PREFIX + "resultInterceptors";
    public static final String OPTION_DEFAULT_LABELFILE = SYSTEMOPTION_PREFIX + "defaultlabelbundle:";
    public static final String OPTION_DEFAULT_LABELCONTAINER = SYSTEMOPTION_PREFIX + "defaultlabelcontainer:";
    public static final String OPTION_DEFAULT_LABELLANGUAGE = SYSTEMOPTION_PREFIX + "defaultlabellanguage";
    public static final String OPTION_INNER_LAYOUT = SYSTEMOPTION_PREFIX + "innerlayout";
    public static final String OPTION_PORTLET_EVENT_STARTINDEX = SYSTEMOPTION_PREFIX + "portletEventStartIndex";
    public static final String OPTION_WEBTML_SCOPE = SYSTEMOPTION_PREFIX + "webtmlScope";
    public static final String OPTION_IMAGE_DERIVATES = SYSTEMOPTION_PREFIX + "imageDerivates";

    public static final String OPTION_MODULE_CONTROLLER = SYSTEMOPTION_PREFIX + "moduleController";
    public static final String OPTION_META_MAPPINGS = SYSTEMOPTION_PREFIX + "metaMappings";
    public static final String OPTION_ITEM_MAPPINGS = SYSTEMOPTION_PREFIX + "itemMappings";
    public static final String OPTION_INCLUDELEVEL = SYSTEMOPTION_PREFIX + "includeLevel";

    // Collects all options that should not be recovered in AJAX requests
    public static final Set<String> UNRECOVERABLE_OPTIONS = new HashSet<String>();
    static {
        UNRECOVERABLE_OPTIONS.add(Base.OPTION_PORTLET_EVENT_STARTINDEX);
        UNRECOVERABLE_OPTIONS.add(Base.OPTION_DESIGNDB);
        UNRECOVERABLE_OPTIONS.add(Base.OPTION_TMLMODULE_NAME);
        UNRECOVERABLE_OPTIONS.add(Base.OPTION_TMLMODULE_MEDIAKEY);
        UNRECOVERABLE_OPTIONS.add(Base.OPTION_MODULE_CONTROLLER);
    }

    public static final String URL_VERSION_PARAMETER = "?version=" + WGAVersion.WGAPUBLISHER_MAJOR_VERSION + "-"
            + WGAVersion.WGAPUBLISHER_MINOR_VERSION + "-" + WGAVersion.WGAPUBLISHER_MAINTENANCE_VERSION;

    public static final String TAGVALUE_STATUS = "TagStatus";
    public static final String TAGVALUE_DYNAMIC_ATTRIBUTES = "TagDynamicAttributes";

    public static final String ATTRIB_INCLUDEPARENT = Include.class.getName() + ":IncludeParent";

    public static final String AJAX_MODE_NO_PORTLET_REFRESH = "norefresh";

    public BaseTagStatus getStatus() {
        return (BaseTagStatus) getValue(TAGVALUE_STATUS);
    }

    protected BaseTagStatus createTagStatus() {
        return new BaseTagStatus();
    }

    // Generic tag attributes
    private String context = null;
    private String privateContext = null;
    private String output = null;
    private String var = null;
    private String _if = null;
    private String _unless = null;
    private String sessionvar = null;
    private String appendvar = null;
    private String divider = null;
    private String format = null;
    protected String encode = null;
    private String sourceline = null;
    private String trim = null;
    private String _wrap = null;

    // Tags environment

    public void setResult(Object result) {
        getStatus().result = result;
    }

    protected Base appendResult(String tmlResult) throws WGAPIException {
        getStatus().appendResult(tmlResult);
        return this;
    }

    protected void clearResult() {

        getStatus().result = null;

    }

    public void addWarning(String message, boolean cancelTag) {
        getStatus().addWarning(getTMLContext(), message, cancelTag, null);
    }

    public void addWarning(String message) {
        addWarning(message, false);
    }

    public void clearWarnings() {
        this.pageContext.setAttribute(Base.class.getName() + ":Warnings", null, PageContext.REQUEST_SCOPE);
    }

    public TMLContext getMainContext() {

        // Wayyyyyy easier and faster....
        return getTMLContext().getmaincontext();

        //return this.getTMLContextForDocument((WGContent) this.pageContext.getRequest().getAttribute(WGACore.ATTRIB_MAINCONTEXT));
    }

    public TMLContext getTMLContextForDocument(WGDocument doc) {
        return getTMLContext().getTMLContextForDocument(doc);
    }

    public boolean stringToBoolean(String expr) {
        try {
            return WGUtils.stringToBoolean(expr);
        } catch (IllegalArgumentException e) {
            addWarning(e.getMessage(), false);
            return false;
        }
    }

    public int stringToInteger(String expr, int defaultValue) {

        if (expr == null) {
            return defaultValue;
        }

        try {
            int result = WGUtils.parseInt(expr);
            if (result == Integer.MAX_VALUE) { // Most likely because input contained something evaluating to "Infinity" (#00004187)
                this.addWarning("WebTML attribute evaluates to positive infinity, which is invalid: " + expr
                        + ". Falling back to default: " + defaultValue);
                return 0;
            }
            return result;
        } catch (NumberFormatException exc) {
            this.addWarning("Cannot interpret WebTML attribute as number: " + expr + ". Falling back to default: "
                    + defaultValue);
            return defaultValue;
        }
    }

    public BaseTagStatus getTagStatusById(String id) {
        return getStatus().getTagStatusById(id);
    }

    public BaseTagStatus getTagStatusById(String id, Class<?> tagClass) {
        return getStatus().getTagStatusById(id, tagClass);
    }

    /**
     * @see BodyTagSupport#doStartTag()
     */
    public final int doStartTag() throws JspException {

        try {

            // Basic initializations of status
            BaseTagStatus status = initializeStatus();

            // Eventually create debug node
            Element parentDebugNode = getParentTagDebugNode();
            if (parentDebugNode != null) {
                createDebugNode(parentDebugNode);
                status.iterationDebugNode = status.debugNode.addElement("starttag");
            }

            // First get tml context to allow script execution (maybe already needed in id-calculation)
            TMLContext parentContext = this.getParentTagContext();
            TMLContext baseContext = null;

            // We have no parent tag, so we are absolute root: Read main context from thread main context and construct WebTML page version of it
            if (parentContext == null) {
                TMLContext mainContext = TMLContext.getThreadMainContext();
                baseContext = new TMLContext(mainContext.content(), this);
                baseContext.makeThreadMainContext();
                status.baseContext = baseContext;

                // Import vars
                baseContext.importEnvironmentData(mainContext);

                // Import options from independent main context.
                for (String optionName : mainContext.getDesignContext().getOptionNames()) {
                    TMLOption option = mainContext.getDesignContext().getOption(optionName);
                    baseContext.setoption(option.getName(), option.getValue(), option.getScope());
                }

            } else {
                baseContext = new TMLContext(parentContext, status);
            }

            this.setTMLContext(baseContext);

            // Register status with id
            String id = this.getId();
            if (id != null) {
                status.id = id;
                Map<String, BaseTagStatus> tagIds = status.getTagIds();
                tagIds.put(id, status);
            }

            TMLContext tmlContext = null;

            // Set tag contexts
            status.childTMLContext = baseContext;

            if (this.getContext() != null) {
                tmlContext = baseContext.context(this.getContext(), false);
                if (tmlContext != null) {
                    this.setTMLContext(tmlContext);
                    this.setChildTagContext(tmlContext);
                } else {
                    status.subContextError = true;

                    // Tag should not be canceled if the context validity
                    // is checked via iscontextvalid
                    boolean cancelTag = true;
                    if (this instanceof ConditionBase) {
                        ConditionBase conditionTag = (ConditionBase) this;
                        if (conditionTag.getIscontextvalid() != null) {
                            cancelTag = false;
                        }
                    }
                    if (cancelTag == true) {
                        String msg = "Failed context change: " + getContext();
                        if (baseContext.getlasterror() != null) {
                            msg += ". Reason: " + baseContext.getlasterror();
                        }
                        this.addWarning(msg, true);
                        setCancelTag(true);
                        return SKIP_BODY;
                    }

                }
            }

            if (isRequestCancelled()) {
                return SKIP_BODY;
            }

            if (this.getPrivatecontext() != null) {
                tmlContext = baseContext.context(this.getPrivatecontext());
                if (baseContext.getlasterror() == null) {
                    this.setTMLContext(tmlContext);
                } else {
                    this.addWarning(tmlContext.getlasterror(), false);
                }
            }

            // Set boolean flags
            this.setEvalBody(true);

            if (this.getVar() != null || this.getPvar() != null || this.getSessionvar() != null
                    || this.getAppendvar() != null) {
                if (this.getOutput() != null && this.stringToBoolean(this.getOutput()) == true) {
                    this.setResultOutput(true);
                } else {
                    this.setResultOutput(false);
                }
            } else {
                if (this.getOutput() != null && this.stringToBoolean(this.getOutput()) == false) {
                    this.setResultOutput(false);
                } else {
                    this.setResultOutput(true);
                }
            }

            if (status.iterationDebugNode != null) {
                status.iterationDebugNode.addAttribute("startedTagSpecific",
                        DEBUG_TIMESTAMP_FORMAT.format(new Date()));
            }

            // Let status object initialize its attribute delegates, needed for tag-specific processing
            status.initAttributeDelegates(this);

            // Check condition attributes

            if (!mayExecute()) {
                status.cancelTag = true;
                return BodyTag.SKIP_BODY;
            }

            // Execute specialized tml function
            try {
                this.tmlStartTag();
            } catch (TMLException exc) {
                handleTMLException(exc);
            } catch (Exception exc) {
                log.error("Error in tml processing", exc);
                this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
                this.setCancelTag(true);
            }

            if (status.iterationDebugNode != null) {
                status.iterationDebugNode.addAttribute("endedTagSpecific",
                        DEBUG_TIMESTAMP_FORMAT.format(new Date()));
            }

            status.determineOutputType(this);
            if (status.directOutput && status.result != null) {
                writeOutput();
                status.result = null;
            }

            if (status.debugNode != null && status.directOutput) {
                status.debugNode.addAttribute("directOutput", "true");
            }

            // Evaluate body, if not denied
            if (this.isEvalBody() && this.getCancelTag() == false) {

                try {
                    this.tmlInitBody();
                    this.iterationIncrement();
                } catch (TMLException exc) {
                    try {
                        handleTMLException(exc);
                    } catch (HttpErrorException e) {
                        getPageContext().getRequest().setAttribute(WGACore.ATTRIB_EXCEPTION, e);
                        throw new JspException("Page rendering canceled bc. of HTTP error output");
                    }
                } catch (Exception exc) {
                    log.error("Error in tml processing", exc);
                    this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
                    this.setCancelTag(true);
                } catch (Error exc) {
                    log.error("Error in tml processing", exc);
                    this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
                    this.setCancelTag(true);
                }

                if (status.directOutput) {
                    pageContext.getOut().write(getPrefix());
                    return BodyTag.EVAL_BODY_INCLUDE;
                } else {
                    return BodyTag.EVAL_BODY_BUFFERED;
                }

            } else {
                return Tag.SKIP_BODY;
            }

        } catch (HttpErrorException exc) {
            getPageContext().getRequest().setAttribute(WGACore.ATTRIB_EXCEPTION, exc);
            throw new JspException("Page rendering canceled bc. of HTTP error output");
        } catch (Exception exc) {
            log.error("Error in tml processing", exc);
            this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
            this.setCancelTag(true);
            return Tag.SKIP_BODY;
        } catch (Error exc) {
            log.error("Error in tml processing", exc);
            this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
            this.setCancelTag(true);
            return Tag.SKIP_BODY;
        }
    }

    private boolean mayExecute() throws WGException {

        String ifTerm = getIf();
        String unlessTerm = getUnless();

        if (ifTerm != null) {
            DynamicAttribute ifEquals = getStatus().dynamicOptions.get("if_equals");
            DynamicAttribute ifIn = getStatus().dynamicOptions.get("if_in");
            if (!evaluateItemConditionAttribute(ifTerm, ifEquals, ifIn))
                return false;
        }

        if (unlessTerm != null) {
            DynamicAttribute unlessEquals = getStatus().dynamicOptions.get("unless_equals");
            DynamicAttribute unlessIn = getStatus().dynamicOptions.get("unless_in");
            if (evaluateItemConditionAttribute(unlessTerm, unlessEquals, unlessIn))
                return false;
        }

        return true;

    }

    /**
      * @param condition
      * @param equalsAttribute
      * @param inAttribute
      * @return true|false
      * @throws WGException
      * @throws WGIllegalArgumentException
      */
    protected boolean evaluateItemConditionAttribute(String condition, DynamicAttribute equalsAttribute,
            DynamicAttribute inAttribute) throws WGException, WGIllegalArgumentException {

        // We want to equal the items from the condition to values given comma-separated
        if (equalsAttribute != null) {

            // Equality to a list of item expressions
            if (equalsAttribute.getValueType() == DynamicAttributeValueType.ITEM_EXPRESSION) {
                if (new BooleanItemExpression(condition, equalsAttribute.getValue())
                        .isTrue(getTMLContext()) == false) {
                    return false;
                }
            }

            // Equality to a constant string value
            else {
                if (new BooleanItemExpression(condition).equalsValue(getTMLContext(),
                        equalsAttribute.getValue()) == false) {
                    return false;
                }
            }

        }

        // We want to have the items in a range of values, specified as lists or individual items (item lists can be given, where each list element is evaluated)
        else if (inAttribute != null) {

            List<Object> values = new ArrayList<>();
            if (inAttribute.getValueType() == DynamicAttributeValueType.ITEM_EXPRESSION) {
                for (String itemName : WGUtils.deserializeCollection(inAttribute.getValue(), ",", true, '\'',
                        false)) {
                    Object value = getTMLContext().item(itemName);
                    if (value instanceof java.util.Collection<?>) {
                        values.addAll((java.util.Collection<?>) value);
                    } else if (value instanceof Map<?, ?>) {
                        values.addAll(((Map<?, ?>) value).values());
                    } else {
                        values.add(value);
                    }
                }
            } else {
                // A little pointless, but valid according to the spec
                values.add(inAttribute.getValue());
            }

            if (new BooleanItemExpression(condition).equalsValues(getTMLContext(), values) == false) {
                return false;
            }

        }

        // We want the items from the condition to evaluate to boolean true
        else {
            if (new BooleanItemExpression(condition).isTrue(getTMLContext()) == false) {
                return false;
            }
        }

        return true;
    }

    private boolean isRequestCancelled() {
        Boolean cancelled = (Boolean) getPageContext().getRequest().getAttribute(WGACore.ATTRIB_REQUEST_CANCELLED);
        return (cancelled != null && cancelled.booleanValue());
    }

    private BaseTagStatus initializeStatus() {
        BaseTagStatus status = createTagStatus();
        setValue(TAGVALUE_STATUS, status);
        status.tagClass = getClass();
        status.retrieveParentTag(this);
        status.tagOptions = this.getParentTagOptions();
        status.localTagOptions = new HashMap<String, Object>();
        status.trimResultString = isTrimResultString();
        status.pageContext = getPageContext();
        String sourceLineStr = getSourceline();
        if (sourceLineStr != null) {
            status.sourceLine = Integer.parseInt(getSourceline());
        }

        if (this instanceof DynamicAttributes) {
            @SuppressWarnings("unchecked")
            Map<String, DynamicAttribute> dynaAtts = (Map<String, DynamicAttribute>) getValue(
                    TAGVALUE_DYNAMIC_ATTRIBUTES);
            if (dynaAtts != null) {
                status.dynamicOptions = dynaAtts;
                removeValue(TAGVALUE_DYNAMIC_ATTRIBUTES);
            }
        }

        return status;
    }

    public BaseTagStatus getParentTag() {
        return getStatus().getParentTag();
    }

    /**
     * @return
     */
    private Element getParentTagDebugNode() {
        BaseTagStatus parent = getParentTag();
        if (parent != null) {
            return parent.iterationDebugNode;
        } else {
            return null;
        }
    }

    protected void writeOutput() {

        try {
            String result = this.getResultString();
            this.pageContext.getOut().write(result);
        } catch (java.io.IOException exc) {
            log.error("Error writing tml output", exc);
        }
    }

    private TMLContext getParentTagContext() throws WGAPIException {
        BaseTagStatus parent = this.getParentTag();
        if (parent != null) {
            return parent.childTMLContext;
        } else {
            return null;
        }
    }

    private Map<String, TMLOption> getParentTagOptions() {

        BaseTagStatus parent = this.getParentTag();

        if (parent != null) {
            return new HashMap<String, TMLOption>(parent.tagOptions);
        } else {
            return new HashMap<String, TMLOption>();
        }
    }

    protected String getTagAttributeValue(String att, String value, String defaultValue) {

        String attResult = null;

        // Nonexistent attribute
        if (value == null) {
            return defaultValue;
        }

        // Empty attribute
        if (value.length() == 0) {
            return value;
        }

        char firstChar = value.charAt(0);
        char lastChar = value.charAt(value.length() - 1);

        // Normal dynamic attribute
        if (firstChar == '{' && lastChar == '}') {
            attResult = resolveDynamicAttribute(value, defaultValue);
        }

        // Mixed attribute: Dynamic parts are contained in {}
        else if (firstChar == '[' && lastChar == ']') {
            attResult = resolveMixedAttribute(value);
        } else {
            attResult = value;
        }

        // Trace debug information
        Element debugNode = getStatus().debugNode;
        if (debugNode != null && attResult != null) {
            Element attElement = (Element) debugNode.selectSingleNode("attribute[@name='" + att + "']");
            if (attElement == null) {
                attElement = debugNode.addElement("attribute");
                attElement.addAttribute("name", att);
                attElement.addCDATA(attResult);
            }
        }

        return attResult;

    }

    private String resolveMixedAttribute(String value) {

        // Cutoff []
        value = value.substring(1, value.length() - 1);
        MixedAttributeResolver resolver = new MixedAttributeResolver();
        return WGUtils.strReplace(value, "{", resolver, true);

    }

    private String resolveDynamicAttribute(String value, String defaultValue) {

        String attResult = null;

        String lcValue = value.toLowerCase();
        if (lcValue.startsWith("{tag:")) {
            String tagId = value.substring(5, value.length() - 1).trim();
            BaseTagStatus tag = this.getTagStatusById(tagId);
            if (tag == null) {
                this.addWarning("Could not retrieve tag: " + tagId, false);
                attResult = "";
            }

            if (tag.result instanceof java.util.Collection) {
                attResult = WGUtils.serializeCollection((java.util.Collection<?>) tag.result, ",");
            } else {
                attResult = String.valueOf(tag.result);
            }
        } else if (lcValue.startsWith("{item:")) {
            String itemName = value.substring(6, value.length() - 1).trim();
            Object obj = null;
            try {
                obj = this.getTMLContext().itemlist(itemName);
                attResult = de.innovationgate.utils.WGUtils.serializeCollection((List<?>) obj, ",");
            } catch (WGAPIException e) {
                this.addWarning("Could not retrieve item: " + itemName + " bc. of exception: "
                        + e.getClass().getName() + " message: " + e.getMessage(), false);
                getTMLContext().getlog().error("Exception retrieving item: " + itemName, e);
                attResult = "";
            }
            if (obj == null) {
                this.addWarning("Could not retrieve item: " + itemName, false);
                attResult = "";
            }
        } else if (lcValue.startsWith("{meta:")) {
            String metaType = "content";
            String metaName = value.substring(6, value.length() - 1).trim();
            int slashPos = metaName.indexOf("/");
            if (slashPos != -1) {
                metaType = metaName.substring(0, slashPos).trim();
                metaName = metaName.substring(slashPos + 1).trim();
            }

            Object obj = null;
            try {
                obj = this.getTMLContext().metalist(metaType, metaName);
                attResult = de.innovationgate.utils.WGUtils.serializeCollection((List<?>) obj, ",");
            } catch (WGAPIException e) {
                this.addWarning("Could not retrieve meta: " + metaType + "/" + metaName + " bc. of exception: "
                        + e.getClass().getName() + " message: " + e.getMessage(), false);
                getTMLContext().getlog().error("Exception retrieving meta: " + metaType + "/" + metaName, e);
                attResult = "";
            }
            if (obj == null) {
                this.addWarning("Could not retrieve meta: " + metaType + "/" + metaName, false);

                attResult = "";
            }

        } else if (lcValue.startsWith("{label:")) {
            String labelName = value.substring(7, value.length() - 1).trim();
            int paramStartPos = labelName.indexOf("(");
            int paramEndPos = labelName.indexOf(")");
            List<String> params = null;
            if (paramStartPos != -1 && paramEndPos != -1) {
                params = WGUtils.deserializeCollection(labelName.substring(paramStartPos + 1, paramEndPos), ",",
                        true);
                labelName = labelName.substring(0, paramStartPos);
            }
            attResult = getTMLContext().label(labelName, params);
        } else if (lcValue.startsWith("{option:")) {
            String optionName = value.substring(8, value.length() - 1).trim();
            Object option = this.getTMLContext().option(optionName);
            if (option == null) {
                this.addWarning("Could not retrieve option: " + optionName, false);
                attResult = "";
            } else {
                attResult = String.valueOf(option);
            }
        } else if (lcValue.startsWith("{plugin:")) {
            String pluginName = value.substring(8, value.length() - 1).trim();
            attResult = getTMLContext().plugindbkey(pluginName);
        } else if (lcValue.startsWith("{scoped:")) {
            String str = value.substring(8, value.length() - 1).trim();
            try {
                return WGA.get(getTMLContext()).scoped(str);
            } catch (WGException e) {
                this.addWarning("Could not scope string bc. of exception: " + e.getClass().getName() + " message: "
                        + e.getMessage(), false);
                getTMLContext().getlog().error("Exception scoping string", e);
            }
        } else if (value.length() > 0) {

            String expression = value.substring(1, value.length() - 1);
            de.innovationgate.wgpublisher.expressions.ExpressionEngine engine = de.innovationgate.wgpublisher.expressions.ExpressionEngineFactory
                    .getEngine(this.getDefaultExpressionLanguage());

            Map<String, Object> customObjects = new HashMap<String, Object>();
            customObjects.put("VOID", VoidAttribute.INSTANCE);

            de.innovationgate.wgpublisher.expressions.ExpressionResult result = engine.evaluateExpression(
                    expression, getTMLContext(), ExpressionEngine.TYPE_EXPRESSION, customObjects);
            if (result.isError()) {
                addExpressionWarning(expression, result);
                attResult = "";
            }

            Object resultValue = result.getResult();
            if (resultValue == null) {
                attResult = "";
            } else if (resultValue == VoidAttribute.INSTANCE) {
                return defaultValue;
            } else if (resultValue instanceof java.util.Collection) {
                attResult = de.innovationgate.utils.WGUtils
                        .serializeCollection((java.util.Collection<?>) resultValue, ",");
            } else {
                attResult = resultValue.toString();
            }
        } else {
            attResult = "";
        }
        return attResult;
    }

    /**
     * @see BodyTagSupport#doEndTag()
     */
    public final int doEndTag() throws JspException {

        BaseTagStatus status = getStatus();

        try {

            if (getTMLContext() == null) {
                if (this instanceof Root) {
                    return SKIP_PAGE;
                } else {
                    return EVAL_PAGE;
                }
            }

            if (this.getCancelTag() == true) {
                return EVAL_PAGE;
            }

            // Cancel if request was cancelled
            if (isRequestCancelled()) {
                return SKIP_PAGE;
            }

            if (status.debugNode != null) {
                status.iterationDebugNode = status.debugNode.addElement("endtag");
                status.iterationDebugNode.addAttribute("started", DEBUG_TIMESTAMP_FORMAT.format(new Date()));
            }

            if (status.iterationDebugNode != null) {
                status.iterationDebugNode.addAttribute("startedTagSpecific",
                        DEBUG_TIMESTAMP_FORMAT.format(new Date()));
            }

            try {
                this.tmlEndTag();
            } catch (TMLException exc) {
                handleTMLException(exc);
            } catch (Exception exc) {
                log.error("Error in tml processing", exc);
                this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
                this.setCancelTag(true);
            }

            if (status.iterationDebugNode != null) {
                status.iterationDebugNode.addAttribute("endedTagSpecific",
                        DEBUG_TIMESTAMP_FORMAT.format(new Date()));
            }

            if (this.getCancelTag() == true) {
                return EVAL_PAGE;
            }

            if (status.writeVars) {
                if (this.getVar() != null) {
                    this.getTMLContext().setvar(this.getVar(), optionallyReduceListValue(status.result), false);
                }

                if (this.getSessionvar() != null) {
                    this.getTMLContext().setSessionVar(this.getSessionvar(),
                            optionallyReduceListValue(status.result), false, false);
                }

                if (this.getPvar() != null) {
                    this.getTMLContext().getportlet().setvar(this.getPvar(),
                            optionallyReduceListValue(status.result));
                }
                if (this.getPsessionvar() != null) {
                    this.getTMLContext().getportlet().setsessionvar(this.getPsessionvar(),
                            optionallyReduceListValue(status.result), false, false);
                }

                if (this.getAppendvar() != null) {
                    this.getTMLContext().appendvar(this.getAppendvar(), status.result);
                }
            }

            if (this.isResultOutput() == true) {
                if (status.directOutput) {
                    pageContext.getOut().write(getSuffix());
                } else if (status.result != null) {
                    this.writeOutput();
                }
            }

            @SuppressWarnings("unchecked")
            List<ResultInterceptor> interceptors = (List<ResultInterceptor>) getOption(OPTION_RESULT_INTERCEPTORS);
            if (interceptors != null) {
                Iterator<ResultInterceptor> icIt = interceptors.iterator();
                while (icIt.hasNext()) {
                    ResultInterceptor i = icIt.next();
                    if (!i.interceptResult(status)) {
                        icIt.remove();
                    }
                }
            }

            if (this.getId() == null && status.keepResult == false) {
                status.result = null;
            }

            if (status.debugNode != null) {
                status.debugNode.addAttribute("ended", DEBUG_TIMESTAMP_FORMAT.format(new Date()));
                status.debugNode.addAttribute("subtags", new Integer(status.subtags).toString());

                status.duration = (System.currentTimeMillis() - status.starttime);
                status.processingTime += status.duration;
                status.debugNode.addAttribute("duration", new Long(status.duration).toString());
                status.debugNode.addAttribute("processingtime", new Long(status.processingTime).toString());

                BaseTagStatus parent = getParentTag();
                if (parent != null) {
                    parent.addSubtags(status.subtags + 1);
                    parent.processingTime -= status.duration;
                }

            }
            return EVAL_PAGE;
        } catch (Exception exc) {
            log.error("Error in tml processing", exc);
            this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
            this.setCancelTag(true);
            return EVAL_PAGE;
        } catch (Error exc) {
            log.error("Error in tml processing", exc);
            this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
            this.setCancelTag(true);
            return EVAL_PAGE;
        } finally {
            // Absolute must-do cleanup operations of the tag
            try {
                tmlCleanup();
            } catch (Exception e) {
                log.error("Error in tml tag cleanup", e);
                this.addWarning("Exception on tag cleanup: " + e.getClass().getName() + ":" + e.getMessage(), true);
            }

            if (status.baseContext != null) {
                status.baseContext.removeThreadMainContext();
            }

            // Remove the tag status for memory reasons
            removeValue(TAGVALUE_STATUS);
        }

    }

    private Object optionallyReduceListValue(Object result) {

        if (getTMLContext().getDesignContext().getVersionCompliance().isAtLeast(7, 2) && result instanceof List) {
            List<?> list = (List<?>) result;
            if (list.size() == 0) {
                return null;
            } else if (list.size() == 1) {
                return list.get(0);
            }
        }

        return result;

    }

    public void addResultInterceptor(ResultInterceptor ic) {

        @SuppressWarnings("unchecked")
        List<ResultInterceptor> interceptors = (List<ResultInterceptor>) getOption(OPTION_RESULT_INTERCEPTORS);
        if (interceptors == null) {
            interceptors = new ArrayList<>();
            getStatus().setOption(OPTION_RESULT_INTERCEPTORS, interceptors, TMLOption.SCOPE_LOCAL);
        }

        interceptors.add(ic);

    }

    /**
     * Method to overwrite for cleanup operations of the tag, that should be executed in any case
     * after tag processing, even when an error occurs
     */
    protected void tmlCleanup() {
    }

    /**
     * @see BodyTagSupport#doAfterBody()
     */
    public final int doAfterBody() throws JspException {

        try {

            BaseTagStatus status = getStatus();

            if (getTMLContext() == null) {
                return SKIP_BODY;
            }

            if (isRequestCancelled()) {
                return SKIP_BODY;
            }
            if (!status.directOutput) {
                this.appendResult(this.getBodyContent().getString());
                this.getBodyContent().clearBody();
            }
            this.setEvalBody(false);

            try {
                this.tmlAfterBody();
            } catch (TMLException exc) {
                handleTMLException(exc);
            } catch (Exception exc) {
                log.error("Error in tml processing", exc);
                this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
                this.setCancelTag(true);
            }

            if (this.isEvalBody() && this.getCancelTag() == false) {
                iterationIncrement();
                if (status.directOutput) {
                    pageContext.getOut().write(getDivider());
                    return EVAL_BODY_AGAIN;
                } else {
                    return EVAL_BODY_BUFFERED;
                }
            } else {
                if (status.iterationDebugNode != null) {
                    status.iterationDebugNode.addAttribute("ended", DEBUG_TIMESTAMP_FORMAT.format(new Date()));
                }
                return SKIP_BODY;
            }
        } catch (Exception exc) {
            log.error("Error in tml processing", exc);
            this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
            this.setCancelTag(true);
            return SKIP_BODY;
        } catch (Error exc) {
            log.error("Error in tml processing", exc);
            this.addWarning(exc.getClass().getName() + ":" + exc.getMessage(), true);
            this.setCancelTag(true);
            return SKIP_BODY;
        }
    }

    private void handleTMLException(TMLException exc) throws JspException, HttpErrorException {

        // If root canceled we need to exit before anything else. WebTML environment may not be ready.
        if (this instanceof Root && exc.isCancelTag()) {
            throw new HttpErrorException(HttpServletResponse.SC_BAD_REQUEST, exc.getMessage(), null);
        }

        if (!(exc instanceof TMLSilentCancelException)) {
            this.addWarning(exc.getMessage(), exc.isCancelTag());
            if (exc.getRootCause() != null) {
                log.error("Exception in WebTML warning: " + exc.getMessage(), exc.getRootCause());
            }
        }

        if (exc.isCancelTag()) {
            this.setCancelTag(true);
        }

    }

    public void tmlStartTag() throws TMLException, WGException {
    };

    public void tmlAfterBody() throws TMLException, WGException {
    };

    public void tmlInitBody() throws TMLException, WGException {
    };

    public void tmlEndTag() throws TMLException, WGException {
    };

    /**
     * Gets the context
     * 
     * @return Returns a String
     */
    public String getContext() {
        return this.getTagAttributeValue("context", context, null);
    }

    /**
     * Sets the context
     * 
     * @param context
     *            The context to set
     */
    public void setContext(String context) {
        this.context = context;
    }

    /**
     * Gets the id
     * 
     * @return Returns a String
     */
    public String getId() {
        return this.getTagAttributeValue("id", id, null);
    }

    /**
     * Gets the output
     * 
     * @return Returns a String
     */
    public String getOutput() {
        return this.getTagAttributeValue("output", output, null);
    }

    /**
     * Sets the output
     * 
     * @param output
     *            The output to set
     */
    public void setOutput(String output) {
        this.output = output;
    }

    /**
     * Gets the privateContext
     * 
     * @return Returns a String
     */
    public String getPrivatecontext() {
        return this.getTagAttributeValue("privatecontext", privateContext, null);
    }

    /**
     * Sets the privateContext
     * 
     * @param privateContext
     *            The privateContext to set
     */
    public void setPrivatecontext(String tmlContext) {
        this.privateContext = tmlContext;
    }

    /**
     * Gets the var
     * 
     * @return Returns a String
     */
    public String getVar() {
        return this.getTagAttributeValue("var", var, null);
    }

    /**
     * Sets the var
     * 
     * @param var
     *            The var to set
     */
    public void setVar(String var) {
        this.var = var;
    }

    /**
     * Gets the result
     * 
     * @return Returns a String
     */
    protected Object getResult() {
        return getStatus().result;
    }

    public String getResultString() {
        return getResultString(true);
    }

    public String getResultString(boolean includeFormatting) {

        BaseTagStatus status = getStatus();
        String result = status.getResultString(includeFormatting, stringToBoolean(getTrim()));
        String _w = getWrap();

        if (result.length() > 0 && _w != null && !_w.isEmpty() && includeFormatting) {
            try {
                return "<" + _w + buildDynamicHtmlAttributes("wrap") + ">" + result + "</" + _w + ">";
            } catch (WGException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    public WGDatabase openContentDB(String key) throws WGException {
        return this.getCore().openContentDB(getTMLContext().resolveDBKey(key),
                (HttpServletRequest) pageContext.getRequest());
    }

    public javax.servlet.jsp.PageContext getPageContext() {
        return this.pageContext;
    }

    public void iterationIncrement() {
        BaseTagStatus status = getStatus();
        status.iteration++;
        if (status.debugNode != null) {
            if (status.iterationDebugNode != null) {
                status.iterationDebugNode.addAttribute("ended", DEBUG_TIMESTAMP_FORMAT.format(new Date()));
            }
            status.iterationDebugNode = status.debugNode.addElement("body");
            status.iterationDebugNode.addAttribute("started", DEBUG_TIMESTAMP_FORMAT.format(new Date()));
            status.iterationDebugNode.addAttribute("iteration", String.valueOf(status.iteration));
            try {
                status.iterationDebugNode.addAttribute("childcontext", getChildTagContext().getpath());
            } catch (WGAPIException e) {
                status.iterationDebugNode.addAttribute("childcontext",
                        "Error: " + e.getClass().getName() + " message: " + e.getMessage());
            }
            status.iterationDebugNode.addAttribute("ccloadeddocs",
                    String.valueOf(getChildTagContext().db().getSessionContext().getTotalFetchedCores()));
        }
    }

    /**
     * Gets the childTmlContext
     * 
     * @return Returns a TmlContext
     */
    public TMLContext getChildTagContext() {
        return getStatus().childTMLContext;
    }

    /**
     * Sets the childTagContext
     * 
     * @param childTagContext
     *            The childTagContext to set
     */
    public void setChildTagContext(TMLContext childTagContext) {
        this.getStatus().childTMLContext = childTagContext;
    }

    /**
     * Gets the tagContext
     * 
     * @return Returns a TagContext
     */
    public TMLContext getTMLContext() {
        return getStatus().tmlContext;
    }

    /**
     * Sets the tagContext
     * 
     * @param tagContext
     *            The tagContext to set
     */
    public void setTMLContext(TMLContext tagContext) {
        BaseTagStatus status = getStatus();
        status.tmlContext = tagContext;
        if (status.debugNode != null) {
            try {
                status.debugNode.addAttribute("context", getTMLContext().getpath());
            } catch (WGAPIException e) {
                status.debugNode.addAttribute("context",
                        "Error: " + e.getClass().getName() + " message: " + e.getMessage());
            }
        }
    }

    /**
     * Gets the evalBody
     * 
     * @return Returns a boolean
     */
    public boolean isEvalBody() {
        return getStatus().evalBody;
    }

    /**
     * Sets the evalBody
     * 
     * @param evalBody
     *            The evalBody to set
     */
    public void setEvalBody(boolean evalBody) {
        getStatus().evalBody = evalBody;
    }

    public boolean isWriteVars() {
        return getStatus().writeVars;
    }

    public void setWriteVars(boolean writeVars) {
        getStatus().writeVars = writeVars;
    }

    /**
     * Gets the iteration
     * 
     * @return Returns a int
     */
    public int getIteration() {
        return getStatus().iteration;
    }

    /**
     * Sets the iteration
     * 
     * @param iteration
     *            The iteration to set
     */
    public void setIteration(int iteration) {
        getStatus().iteration = iteration;
    }

    /**
     * Gets the resultOutput
     * 
     * @return Returns a boolean
     */
    public boolean isResultOutput() {
        return getStatus().resultOutput;
    }

    /**
     * Sets the resultOutput
     * 
     * @param resultOutput
     *            The resultOutput to set
     */
    public void setResultOutput(boolean resultOutput) {
        getStatus().resultOutput = resultOutput;
    }

    /**
     * Gets the divider
     * 
     * @return Returns a String
     */
    public String getDivider() {
        return this.getTagAttributeValue("divider", divider, "");
    }

    /**
     * Sets the divider
     * 
     * @param divider
     *            The divider to set
     */
    public void setDivider(String divider) {
        this.divider = divider;
    }

    /**
     * Gets the format
     * 
     * @return Returns a String
     */
    public String getFormat() {
        return this.getTagAttributeValue("format", format, null);
    }

    /**
     * Sets the format
     * 
     * @param format
     *            The format to set
     */
    public void setFormat(String format) {
        this.format = format;
    }

    public de.innovationgate.wgpublisher.WGPDispatcher getDispatcher() {
        return (de.innovationgate.wgpublisher.WGPDispatcher) this.pageContext.getServletContext()
                .getAttribute(WGACore.ATTRIB_DISPATCHER);
    }

    public de.innovationgate.wgpublisher.WGACore getCore() {
        return (de.innovationgate.wgpublisher.WGACore) this.pageContext.getServletContext()
                .getAttribute(WGACore.ATTRIB_CORE);
    }

    public String getMimeType() {
        return this.pageContext.getAttribute(WGACore.ATTRIB_MIMETYPE, PageContext.REQUEST_SCOPE).toString();
    }

    /**
     * Gets the encode
     * 
     * @return Returns a String
     */
    public String getEncode() {
        return this.getTagAttributeValue("encode", encode, null);
    }

    /**
     * Sets the encode
     * 
     * @param encode
     *            The encode to set
     */
    public void setEncode(String encode) {
        this.encode = encode;
    }

    public Integer getLevel() {

        BaseTagStatus status = getStatus();
        if (status.level == null) {
            BaseTagStatus parent = this.getParentTag();
            if (parent != null) {
                if (this instanceof Root) {
                    status.level = parent.level;
                } else {
                    status.level = new Integer(parent.level + 1);
                }

            } else {
                status.level = new Integer(0);
            }
        }
        return status.level;
    }

    public String getWGPPath() {
        return getTMLContext().getEnvironment().getPublisherURL();
    }

    public boolean isCollectionsShowReleasedOnly() {

        if (!getTMLContext().isbrowserinterface()) {
            return true;
        }

        Boolean colReleasedOnly = (Boolean) this.pageContext.getSession()
                .getAttribute(WGACore.ATTRIB_BI_COLLECTIONS_SHOW_RELEASED_ONLY);
        if (colReleasedOnly != null && colReleasedOnly.booleanValue() == true) {
            return true;
        } else {
            return false;
        }

    }

    protected boolean isChildContextErrornous() {
        return getStatus().subContextError;
    }

    /**
     * Gets the cancelTag
     * 
     * @return Returns a boolean
     */
    public boolean getCancelTag() {
        return getStatus().cancelTag;
    }

    /**
     * Sets the cancelTag
     * 
     * @param cancelTag
     *            The cancelTag to set
     */
    public void setCancelTag(boolean cancelTag) {
        getStatus().cancelTag = cancelTag;
    }

    public OutputStream getOutputStream() {
        try {
            return this.getResponse().getOutputStream();
        } catch (IOException e) {
            log.error("Error retrieving output stream", e);
            return null;
        }
    }

    public HttpServletResponse getResponse() {

        HttpServletResponse response = (HttpServletResponse) this.pageContext.getRequest()
                .getAttribute(WGACore.ATTRIB_SERVLETRESPONSE);
        if (response != null) {
            return response;
        } else {
            return (HttpServletResponse) getPageContext().getResponse();
        }
    }

    public Object getOption(String optionName) {
        return getStatus().getOption(optionName);
    }

    /**
     * @see javax.servlet.jsp.tagext.Tag#release()
     */
    public void release() {
        removeValue(TAGVALUE_STATUS);
        super.release();
    }

    public TMLUserProfile getUserProfile(WGDatabase db) {
        try {
            return getCore().getPersManager().fetchUserProfile((HttpServletRequest) getPageContext().getRequest(),
                    (HttpServletResponse) getPageContext().getResponse(), db);
        } catch (WGAPIException e) {
            getCore().getLog().error("Unable to retrieve user profile", e);
        }
        return null;
    }

    public TMLUserProfile getUserProfile() {
        return this.getUserProfile(this.getTMLContext().getdocument().getDatabase());
    }

    /**
     * Returns the sessionvar.
     * 
     * @return String
     */
    public String getSessionvar() {
        return this.getTagAttributeValue("sessionvar", sessionvar, null);
    }

    /**
     * Sets the sessionvar.
     * 
     * @param sessionvar
     *            The sessionvar to set
     */
    public void setSessionvar(String sessionvar) {
        this.sessionvar = sessionvar;
    }

    public String getDefaultExpressionLanguage() {

        // Static tml has always expression language tmlscript, as this works for all DB implementations
        String requestType = (String) this.pageContext.getRequest().getAttribute(WGACore.ATTRIB_REQUESTTYPE);
        if (requestType.equals(WGPDispatcher.REQUESTTYPE_STATICTML)) {
            return ExpressionEngineFactory.ENGINE_TMLSCRIPT;
        }

        // Try to retrieve XPL set by Attrib defaultxpl in Range-Tag
        String xplang = (String) this.getOption(Base.OPTION_DEFAULT_XPLANGUAGE);

        // Get XPL by current design db
        if (xplang == null) {
            WGDatabase db = null;
            try {
                db = openContentDB(getDesignDBKey());
            } catch (WGException e) {
            }
            if (db != null) {
                xplang = (String) db.getAttribute(WGACore.DBATTRIB_EXPRESSION_DEFAULT);
            } else {
                return ExpressionEngineFactory.ENGINE_TMLSCRIPT;
            }
        }
        return xplang;

    }

    /*
     * public TMLActionLink createCustomActionLink(Integer actionKey, List
     * params, TMLContext context) { return
     * createActionLink(String.valueOf(actionKey), params, context); }
     */

    private String pvar;

    private String psessionvar;

    /**
     * @param rootElement
     */
    protected void createDebugNode(Element parent) {

        BaseTagStatus status = getStatus();

        status.starttime = System.currentTimeMillis();

        String className = getClass().getName();
        className = className.substring(className.lastIndexOf(".") + 1).toLowerCase();

        status.debugNode = parent.addElement("tmltag");
        status.debugNode.addAttribute("name", className);
        if (this instanceof Root) {
            status.debugNode.addAttribute("resource", ((Root) this).getResource());
            status.debugNode.addAttribute("path", status.debugNode.getUniquePath());
        }
        status.debugNode.addAttribute("sourceline", getSourceline());
        status.debugNode.addAttribute("started", DEBUG_TIMESTAMP_FORMAT.format(new Date()));
        status.debugNode.addAttribute("level", String.valueOf(getLevel()));

        if (status.debugNode.getDocument().getRootElement().attributeValue("traceroptions", "false")
                .equals("false")) {
            return;
        }

        Element options = status.debugNode.addElement("options");
        Iterator<String> opts = status.tagOptions.keySet().iterator();
        String key;
        Element optionElem;
        TMLOption tmloption;
        while (opts.hasNext()) {
            key = (String) opts.next();
            if (!key.startsWith("$")) {
                tmloption = status.tagOptions.get(key);
                optionElem = options.addElement("option");
                optionElem.addAttribute("name", tmloption.getName());
                optionElem.addAttribute("type",
                        (tmloption.getValue() != null ? tmloption.getValue().getClass().getName() : "(none)"));
                optionElem.addText(String.valueOf(tmloption.getValue()));
            }
        }
    }

    /**
     * @param string
     */
    public void setSuffix(String string) {
        getStatus().suffix = string;
    }

    /**
     * @param string
     */
    public void setPrefix(String string) {
        getStatus().prefix = string;
    }

    public String getSuffix() {
        return getStatus().suffix;
    }

    public String getPrefix() {
        return getStatus().prefix;
    }

    public Element getDebugNode() {
        return getStatus().debugNode;
    }

    public Element getIterationDebugNode() {
        return getStatus().iterationDebugNode;
    }

    public String getSourceline() {
        return sourceline;
    }

    public void setSourceline(String string) {
        sourceline = string;
    }

    protected String buildCallActionLink(String sAction, String formID, Map<String, Object> namedParams,
            List<Object> params, String portletMode, String portletContext) throws WGAPIException, TMLException {
        TMLAction tmlAction = getTMLContext().getActionByID(sAction, getDesignDBKey());
        if (tmlAction != null) {
            TMLActionLink actionLinkObj = tmlAction.createActionLink(namedParams, params, getTMLContext());
            actionLinkObj.setPortletmode(portletMode);
            if (portletContext != null) {
                actionLinkObj.setPortletContextPath(getTMLContext(), portletContext);
            }

            String actionLink;

            //if (formBase != null && formBase.getMode().equals(FormInfo.EDIT_MODE)) {
            //--> none editable forms should be submitted to ensure access in tml:action
            try {
                if (formID != null) {
                    actionLink = actionLinkObj.getJavascriptLink(getCore(), formID,
                            getTMLContext().getDesignContext().getVersionCompliance());
                } else {
                    actionLink = actionLinkObj.getJavascriptLink(getCore(), null,
                            getTMLContext().getDesignContext().getVersionCompliance());
                }
            } catch (UnsupportedEncodingException e) {
                throw new TMLException("Exception creating action link", e, true);
            }
            return actionLink;
        } else {
            return null;
        }
    }

    protected void addExpressionWarning(String expression, ExpressionResult result) {
        WGExpressionException exc = result.getException();

        // Put out as WebTML Warning
        String warningMessage = exc.getMessage()
                + (exc.getExpression() != null ? "\nExpression line: " + exc.getExpression()
                        : "\nExpression:\n" + expression);
        if (!WGUtils.isEmpty(exc.getNativeStackTrace())) {
            warningMessage += "\n" + exc.getNativeStackTrace();
        }
        this.addWarning(warningMessage);

        // Put out on log
        if (exc.getCause() != null) {
            getTMLContext().getlog().error("Exception executing tmlscript", exc.getCause());
        } else if (!WGUtils.isEmpty(exc.getNativeStackTrace())) {
            getTMLContext().getlog()
                    .error("Exception executing tmlscript: " + exc.getMessage() + "\n" + exc.getNativeStackTrace());
        }
    }

    /**
     * @return Returns the trim.
     */
    public String getTrim() {
        return getTagAttributeValue("trim", trim, "false");
    }

    /**
     * @param trim
     *            The trim to set.
     */
    public void setTrim(String trim) {
        this.trim = trim;
    }

    public String includeScript(String script) {

        StringBuffer scriptresult = new StringBuffer("");
        WGContent content = this.getTMLContext().content();
        script = script.toLowerCase();

        @SuppressWarnings("unchecked")
        List<String> includedscripts = (List<String>) getPageContext().getRequest().getAttribute("includedscripts");
        if (includedscripts != null && includedscripts.contains(script))
            return "";

        if (script.equals("htmlhead")) {
            scriptresult.append("<script src=\"" + getWGPPath() + "/static/js/htmlhead.js" + URL_VERSION_PARAMETER
                    + "\"></script>\n");
        }

        if (includedscripts == null) {
            includedscripts = new ArrayList<String>();
        }
        includedscripts.add(script);
        getPageContext().getRequest().setAttribute("includedscripts", includedscripts);

        return scriptresult.toString();
    }

    public WGDatabase openDesignDB() throws WGException {

        String designKey = (String) getDesignDBKey();
        if (designKey != null) {
            return this.openContentDB(designKey);
        } else {
            return this.getTMLContext().db();
        }

    }

    public String getPvar() {
        return getTagAttributeValue("pvar", pvar, null);
    }

    public String getPsessionvar() {
        return getTagAttributeValue("psessionvar", psessionvar, null);
    }

    public void setPvar(String pvar) {
        this.pvar = pvar;
    }

    public void setPsessionvar(String pvar) {
        this.psessionvar = pvar;
    }

    public boolean isVarWritten() {
        return this.var != null || this.sessionvar != null || this.pvar != null || this.psessionvar != null
                || this.appendvar != null;
    }

    public String getTagName() {
        String className = getClass().getName();
        return className.substring(className.lastIndexOf(".") + 1).toLowerCase();
    }

    public void buildHTMLHead(boolean metaOutput, String scripts) throws WGException {
        TMLContext context = this.getTMLContext();
        WGContent content = context.content();

        if (content != null && metaOutput == true) {
            // put out meta tags
            this.appendResult("<meta name=\"generator\" content=\"").appendResult(WGACore.getGeneratorString())
                    .appendResult("\">\n");
            this.appendResult("<meta name=\"keywords\" content=\"")
                    .appendResult(de.innovationgate.utils.WGUtils.serializeCollection(content.getKeywords(), ","))
                    .appendResult("\">\n");
        }

        boolean includeHTMLHeadScript = true;
        if (scripts != null) {
            try {
                includeHTMLHeadScript = WGUtils.stringToBoolean(scripts);
            } catch (Exception e) {
                addWarning(e.getMessage(), false);
            }
        }
        if (includeHTMLHeadScript)
            this.appendResult(includeScript("htmlhead"));

        StringBuilder jscodeBuilder = new StringBuilder();

        if (!getWGPPath().isEmpty())
            jscodeBuilder.append("WGA.contextpath=\"" + getWGPPath() + "\";"); // used by htmlhead.js since wga-4

        //jscodeBuilder.append("WGA.uriHash =\"" + getTMLContext().getUriHash() + "\";");

        initPageConnectionClient(jscodeBuilder);

        if (getStatus().debugNode != null) {
            jscodeBuilder.append("WGA.debug = true;");
        }

        String jscode = jscodeBuilder.toString();
        if (!jscode.isEmpty()) {
            this.appendResult("<script id=\"wga-htmlhead\">");
            this.appendResult(jscode);
            this.appendResult("</script>\n");
        }

        // Authoring mode Scripts
        try {
            WGACore wgacore = WGA.get().getCore();
            if (wgacore.getDispatcher()
                    .getBrowsingSecurity(context.content().getDatabase()) > BrowsingSecurity.NO_AUTHORING
                    && context.isbrowserinterface()) {
                this.appendResult("\n<script id=\"wga-cm-contentinfo\">");
                this.appendResult("\nWGA.contentinfo={");
                this.appendResult("\n\tdbkey:\"" + content.getDatabase().getDbReference() + "\"");
                if (!content.isDummy()) {
                    this.appendResult(",");
                    this.appendResult("\n\tstructkey:\"" + content.getStructKey() + "\",");
                    this.appendResult("\n\tcontentkey:\"" + content.getContentKey(true) + "\",");
                    this.appendResult("\n\ttitle:\""
                            + WGUtils.strReplace(WGUtils.strReplace(content.getTitle(), "\"", "\\\"", true),
                                    "script", "sc\"+\"ript", true)
                            + "\",");
                    this.appendResult("\n\tlanguage:\"" + content.getLanguage().getName() + "\"");
                }
                this.appendResult("\n};");
                this.appendResult("\n</script>\n");
                this.appendResult("\n<script>WGA.CMM={sections:{},hasSections:false}</script>\n");
            }
        } catch (WGException e) {
        }

        // Process HTML head inclusion modules
        for (HTMLHeadInclusion inc : getCore().getHtmlHeadInclusions()) {
            try {
                CharSequence result = inc.processInclusion(getTMLContext());
                if (result != null) {
                    appendResult(String.valueOf(result));
                }
            } catch (Throwable e) {
                getTMLContext().getlog()
                        .error("Exception processing HTML head inclusion " + inc.getClass().getName(), e);
            }
        }
    }

    protected void initPageConnectionClient(StringBuilder out) throws WGException {
        TMLPageImpl page = (TMLPageImpl) WGA.get(getTMLContext()).tmlPage();
        PageConnection pageConn = page.getPageConnection(false);
        if (pageConn == null || pageConn.isClientInited()) {
            return;
        }

        WGA wga = WGA.get(getTMLContext());
        URLBuilder url = wga.urlBuilder(wga.server().getBaseURL() + TMLPageWebSocket.PATH);
        url.clearParameters();
        int port = url.getPort();
        if (getTMLContext().getrequest().isSecure()) {
            url.setProtocol("wss");
        } else {
            url.setProtocol("ws");
        }

        // We stay on the same port
        if (port != -1) {
            url.setPort(port);
        }

        boolean noCloseHandler = "true".equals(System.getProperty("de.innovationgate.wga.unittest"));

        out.append("WGA.onload.register(function() {WGA.websocket.start('")
                .append(WGUtils.encodeJS(url.build(true))).append("', '")
                .append(WGUtils.encodeJS(pageConn.getPageId())).append("', '")
                .append(getTMLContext().gethttpsession().getId()).append("', ").append(noCloseHandler)
                .append(")});");

        pageConn.setClientInited(true);
    }

    public String getTagDescription() {

        String requestType = (String) getPageContext().getRequest().getAttribute(WGACore.ATTRIB_REQUESTTYPE);
        if (requestType.equals(WGPDispatcher.REQUESTTYPE_STATICTML)) {
            return "tml:" + getTagName() + " in static WebTML-Module " + getStatus().getRootTag().resource;
        } else {
            return "tml:" + getTagName() + " on line " + getSourceline() + " in WebTML-Module "
                    + getStatus().getTMLModuleName() + "/" + getStatus().getTMLModuleMediaKey() + " ("
                    + getDesignDBKey() + ")";
        }

    }

    public boolean isAjaxRequest() {
        return (getPageContext().getRequest().getAttribute(WGACore.ATTRIB_AJAXINFO) != null);
    }

    protected StringBuffer createItemEditorDeclaration(String itemName, String editor, String label) {

        StringBuffer prefix = new StringBuffer("<div class=\"WGA-Item\">\n");
        prefix.append("<div class=\"WGA-Item-Info\" style=\"display:none\">");
        prefix.append(itemName + "|" + editor);
        prefix.append("</div>\n");

        if (!editor.equalsIgnoreCase("custom")) {
            prefix.append("<div class=\"WGA-Editor-Options\" style=\"display:none\">");
            prefix.append("{" + getResultString(false) + "}");
            prefix.append("</div>\n");

            // Old Style WGA4 Options:
            if (editor.equalsIgnoreCase("rtf")) {
                prefix.append("<div class=\"WGA4-Editor-Options\" style=\"display:none\">");
                prefix.append("{");
                String opt;

                opt = (String) getTMLContext().option("tabelStyleList");
                if (opt != null)
                    prefix.append("tabelStyleList:[" + opt + "],");

                opt = (String) getTMLContext().option("trStyleList");
                if (opt != null)
                    prefix.append("trStyleList:[" + opt + "],");

                opt = (String) getTMLContext().option("tdStyleList");
                if (opt != null)
                    prefix.append("tdStyleList:[" + opt + "],");

                opt = (String) getTMLContext().option("paragraphStyleList");
                if (opt != null)
                    prefix.append("paragraphStyleList:[" + opt + "],");

                opt = (String) getTMLContext().option("showoptions");
                if (opt != null)
                    prefix.append("showoptions:\"" + opt + "\",");

                opt = (String) getTMLContext().option("hideoptions");
                if (opt != null)
                    prefix.append("hideoptions:\"" + opt + "\",");

                prefix.append("dummy:true");
                prefix.append("}");
                prefix.append("</div>\n");
            }
        }

        prefix.append("<div class=\"WGA-Item-Edit\" style=\"display:none\"></div>");
        prefix.append("<div class=\"WGA-Item-Label\" style=\"display:none\">");
        prefix.append(label);
        prefix.append("</div>\n");
        return prefix;
    }

    public String getDesignDBKey() {
        return getStatus().getDesignDBKey();
    }

    /**
     * Determines if AJAX action URLs are to keep URL parameters of original requests 
     */
    public boolean isKeepParamsOnAJAX() {

        TMLDesignContext designContext = getTMLContext().getDesignContext();

        // Try to determine via explicit setting as publisher option
        WGDatabase designDB = designContext.getDesignDB();
        if (designDB != null) {
            String keepParams = (String) designDB.getAttribute(WGACore.DBATTRIB_AJAX_KEEP_URL_PARAMS);
            if (keepParams != null) {
                if (Boolean.TRUE.equals(keepParams)) {
                    return true;
                } else if (Boolean.FALSE.equals(keepParams)) {
                    return false;
                }
            }
        }

        // Determine by design compliance
        if (designContext.getVersionCompliance().isAtLeast(6, 0)) {
            return true;
        } else {
            return false;
        }

    }

    /**
     * Determines if Non-AJAX action URLs are to keep URL parameters of original requests 
     */
    public boolean isKeepParamsOnNonAJAX() {

        TMLDesignContext designContext = getTMLContext().getDesignContext();

        // Design compliance > 6.0 always keeps them
        if (designContext.getVersionCompliance().isAtLeast(6, 0)) {
            return true;
        }

        // Below that it keeps them only on form posts
        else {
            return getStatus().getRelevantForm() != null;
        }

    }

    public String getAppendvar() {
        return getTagAttributeValue("appendvar", appendvar, null);
    }

    public void setAppendvar(String appendvar) {
        this.appendvar = appendvar;
    }

    protected String buildDynamicHtmlAttributes() throws WGException {
        return buildDynamicHtmlAttributes("html");
    }

    protected String buildDynamicHtmlAttributes(String prefix) throws WGException {

        Map<String, DynamicAttribute> dynAtts = getStatus().dynamicOptions;
        if (dynAtts == null) {
            return "";
        }

        Map<String, String> htmlAtts = new HashMap<String, String>();
        for (DynamicAttribute att : dynAtts.values()) {
            if (att.getPrefix().equals(prefix)) {
                Object value = att.getDynamicValue(getTMLContext());
                if (value != null) {
                    htmlAtts.put(att.getBaseName(), String.valueOf(value));
                }
            }
        }

        StringBuilder html = new StringBuilder();
        for (Map.Entry<String, String> htmlAtt : htmlAtts.entrySet()) {
            html.append(" ");
            html.append(htmlAtt.getKey()).append("=\"").append(htmlAtt.getValue()).append("\"");
        }

        return html.toString();

    }

    protected void performInlineInclude(Inline.Pointer inlinePointer) throws TMLException, WGException {

        BaseTagStatus status = (BaseTagStatus) getStatus();
        if (!(status instanceof RootTagReceptor)) {
            throw new TMLException(
                    "Invalid attempt to perform an inline include from a tag, whose status is no RootTagReceptor: "
                            + getClass().getName());
        }
        RootTagReceptor receptor = (RootTagReceptor) status;

        receptor.setStartTime(System.currentTimeMillis());

        DeployedLayout lyt = getCore().getDeployer().getLayoutMappings().get(inlinePointer.getLayoutKey());
        if (lyt == null) {
            throw new TMLException("Unable to read inline '" + inlinePointer.getDescription()
                    + "' as there is no inline deployment", true);
        }

        String mediaKey = lyt.getMainLibMediaKey();
        status.setOption(OPTION_CURRENT_MEDIAKEY, mediaKey, null);
        String tmlResource = lyt.getResourcePath();

        // Do the include
        this.pageContext.setAttribute(ATTRIB_INCLUDEPARENT, status, PageContext.REQUEST_SCOPE);

        TMLOptionPreserver optionPreserver = new TMLOptionPreserver(getTMLContext().getDesignContext());
        optionPreserver.preserve(Base.OPTION_DESIGNDB, inlinePointer.getDesignDB());
        optionPreserver.preserve(Base.OPTION_PORTLET_NAMESPACE, inlinePointer.getPortletKey());
        optionPreserver.preserve(Base.OPTION_MODULE_CONTROLLER, inlinePointer.getModuleController());
        try {
            this.pageContext.include(tmlResource);
        } catch (Exception e) {
            throw new TMLException("Error executing inline '" + inlinePointer.getDescription()
                    + " from tml module \"" + lyt.getMainLibName() + "/" + lyt.getMainLibMediaKey() + "\"", e,
                    false);
        } finally {
            this.pageContext.setAttribute(ATTRIB_INCLUDEPARENT, null, PageContext.REQUEST_SCOPE);
            optionPreserver.restore();
        }

        // Transfer result from included root to this tag
        de.innovationgate.wgpublisher.webtml.Root.Status rootTagStatus = receptor.getRootTagStatus();
        if (rootTagStatus != null && !rootTagStatus.directOutput) {
            this.setResult(rootTagStatus.getResultString(true, false));
            rootTagStatus.clearResult();
        } else {
            throw new TMLException("Could not retrieve result of included resource: " + tmlResource);
        }

    }

    /**
     * Returns if the trim function on this tag should trim the completed result string instead of each result token
     */
    protected boolean isTrimResultString() {
        return false;
    }

    public void defaultSetDynamicAttribute(List<String> prefixes, String uri, String localName, Object value)
            throws JspException {

        for (String prefix : prefixes) {
            if (localName.startsWith(prefix + "_") || localName.startsWith(prefix + "-")) {

                int prefixSize = prefix.length() + 1;
                char divider = localName.charAt(prefix.length());
                String baseName = localName.substring(prefixSize);

                @SuppressWarnings("unchecked")
                Map<String, DynamicAttribute> dynAtts = (Map<String, DynamicAttribute>) getValue(
                        TAGVALUE_DYNAMIC_ATTRIBUTES);
                if (dynAtts == null) {
                    dynAtts = new HashMap<String, DynamicAttribute>();
                    setValue(TAGVALUE_DYNAMIC_ATTRIBUTES, dynAtts);
                }

                dynAtts.put(prefix + "_" + baseName,
                        new DynamicAttribute(localName, prefix, baseName, String.valueOf(value),
                                divider == '-' ? DynamicAttributeValueType.ITEM_EXPRESSION
                                        : DynamicAttributeValueType.STRING));
                return;
            }
        }

        getCore().getLog().warn("Attribute '" + localName + "' is unknown for TML tag <tml:" + getTagName()
                + "> and will be ignored");

    }

    public String getIf() {
        return getTagAttributeValue("if", _if, null);
    }

    public void setIf(String if1) {
        _if = if1;
    }

    public String getUnless() {
        return getTagAttributeValue("unless", _unless, null);
    }

    public void setUnless(String unless) {
        _unless = unless;
    }

    public Map<String, Object> buildNamedActionParameters(boolean serializable) throws WGException {

        Map<String, DynamicAttribute> dynAtts = getStatus().dynamicOptions;
        if (dynAtts == null) {
            return Collections.emptyMap();
        }

        Map<String, Object> params = new HashMap<String, Object>();
        for (DynamicAttribute att : dynAtts.values()) {

            if (att.getPrefix().equals("a")) {

                Object result = att.getDynamicValue(getTMLContext());

                TMLScript tmlscript = WGA.get(getTMLContext()).tmlscript();
                if (serializable && tmlscript.isNativeObject(result)) {
                    result = ExpressionEngineFactory.getTMLScriptEngine().serializeScriptable(getTMLContext(),
                            result);
                }
                params.put(att.getBaseName(), result);
            }

        }
        return params;

    }

    /**
     * creates an javascript functioncall 
     * to call the given action with given params as ajaxAction
     * @param action
     * @param params
     * @param ajaxMode the ajax mode for this action call - valid values: ActionBase.AJAX_MODE_NO_PORTLET_REFRESH
     * @param portletMode the portlet mode to set after action call
     * @return Javascript-Function call    
     * @throws WGAPIException 
     * @throws TMLException 
     * @throws UnsupportedEncodingException 
     */
    protected String getAjaxJSFunction(TMLAction action, Map<String, Object> namedParams, List<Object> params,
            TMLActionCallParameters callParams, boolean keepParams, Map<String, String> additionalDefProps)
            throws WGAPIException, TMLException {

        TMLActionLink actionLink = action.createActionLink(namedParams, params, getTMLContext());

        //F00004242
        String ajaxMode = callParams.getAjaxMode();
        if (ajaxMode != null && ajaxMode.equalsIgnoreCase(Base.AJAX_MODE_NO_PORTLET_REFRESH)) {
            actionLink.setMode(TMLActionLink.MODE_AJAX_NO_PORTLET_REFRESH);
        }

        actionLink.setPortletmode(callParams.getPortletMode());
        if (callParams.getPortletContext() != null) {
            actionLink.setPortletContextPath(getTMLContext(), callParams.getPortletContext());
        }

        String id = (String) this.getOption(Include.OPTION_AJAX_DIVTAG_ID);
        if (id == null) {
            throw new TMLException(
                    "Unable to render AJAX JavaScript Function: We do not seem to be inside an AJAX portlet");
        }

        String strAction;
        try {
            strAction = actionLink.getJavascriptLink(getCore(), callParams.getRelevantForm(),
                    getTMLContext().getDesignContext().getVersionCompliance());
        } catch (UnsupportedEncodingException e) {
            throw new TMLException("Exception creating action link", e, true);
        }

        AjaxActionDefinition actionDef = new AjaxActionDefinition(strAction, id);
        actionDef.setKeepParams(keepParams);
        if (additionalDefProps != null) {
            actionDef.getAdditionalProps().putAll(additionalDefProps);
        }

        //F00004242
        if (ajaxMode != null && ajaxMode.equalsIgnoreCase(Base.AJAX_MODE_NO_PORTLET_REFRESH)) {
            actionDef.setGraydiv(false);
            actionDef.setMode(Base.AJAX_MODE_NO_PORTLET_REFRESH);
        }

        return "WGA.ajax.action(" + actionDef.toJavaScriptObject() + ")";
    }

    /**
     * creates an javascript functioncall 
     * to call the given action with given params as simple action.
     * @param action
     * @param params
     * @param portletMode the portletmode to set after action call
     * @return Javascript-Function call    
     * @throws WGAPIException 
     * @throws UnsupportedEncodingException 
     */
    protected String getJSFunction(TMLAction action, Map<String, Object> namedParams, List<Object> params,
            TMLActionCallParameters callParams, boolean keepParams, Map<String, String> additionalDefProps)
            throws WGAPIException, TMLException {

        TMLActionLink actionLink = action.createActionLink(namedParams, params, getTMLContext());

        actionLink.setPortletmode(callParams.getPortletMode());
        if (callParams.getPortletContext() != null) {
            actionLink.setPortletContextPath(getTMLContext(), callParams.getPortletContext());
        }

        try {
            String strAction = actionLink.getJavascriptLink(getCore(), callParams.getRelevantForm(),
                    getTMLContext().getDesignContext().getVersionCompliance());

            StringBuffer actionCall = new StringBuffer();
            actionCall.append("WGA.action({");

            actionCall.append("action:'").append(strAction).append("'");
            actionCall.append(", keepParams:").append(keepParams);

            if (additionalDefProps != null) {
                for (Map.Entry<String, String> prop : additionalDefProps.entrySet()) {
                    actionCall.append(", " + prop.getKey() + ":" + prop.getValue());
                }
            }

            actionCall.append("})");
            return actionCall.toString();
        } catch (UnsupportedEncodingException e) {
            throw new TMLException("Exception creating action link", e, true);
        }

    }

    public void writePortletEventRegistration(StringBuffer out, TMLAction action,
            TMLActionCallParameters callParams, boolean keepParams, String onEvent)
            throws WGAPIException, TMLException {

        Map<String, String> additionalActionProps = new HashMap<String, String>();
        additionalActionProps.put("portletEvent", "e");

        String jsFunction;
        if (callParams.getAjaxMode() != null && !callParams.getAjaxMode().equals("false")) {
            jsFunction = "function(e){"
                    + getAjaxJSFunction(action, null, null, callParams, keepParams, additionalActionProps) + "}";
        } else {
            jsFunction = "function(e){"
                    + getJSFunction(action, null, null, callParams, keepParams, additionalActionProps) + "}";
        }

        out.append("WGA.event.addListener(\"");
        out.append(getTMLContext().getportlet().getportletkey());
        out.append("\", \"");
        out.append(onEvent);
        out.append("\", " + jsFunction + ");\n");
    }

    @Override
    public final void setDynamicAttribute(String uri, String localName, Object value) throws JspException {
        defaultSetDynamicAttribute(getDynamicAttributePrefixes(), uri, localName, value);
    }

    protected List<String> getDynamicAttributePrefixes() {
        return WGUtils.list("wrap", "if", "unless");
    }

    public String getWrap() {
        return getTagAttributeValue("wrap", _wrap, null);
    }

    public void setWrap(String wrap) {
        _wrap = wrap;
    }

}