Package org.apache.beehive.netui.tags.tree

Source Code of org.apache.beehive.netui.tags.tree.TreeRenderer

/*
* Copyright 2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* $Header:$
*/
package org.apache.beehive.netui.tags.tree;

import org.apache.beehive.netui.util.internal.InternalStringBuilder;

import org.apache.beehive.netui.core.URLCodec;
import org.apache.beehive.netui.pageflow.PageFlowUtils;
import org.apache.beehive.netui.pageflow.internal.AdapterManager;
import org.apache.beehive.netui.pageflow.internal.InternalUtils;
import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils;
import org.apache.beehive.netui.tags.HtmlUtils;
import org.apache.beehive.netui.tags.html.HtmlConstants;
import org.apache.beehive.netui.tags.internal.PageFlowTagUtils;
import org.apache.beehive.netui.tags.javascript.ScriptRequestState;
import org.apache.beehive.netui.tags.rendering.*;
import org.apache.beehive.netui.util.Bundle;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;

abstract public class TreeRenderer implements HtmlConstants
{
    private TagRenderingBase _imageRenderer;
    private TagRenderingBase _anchorRenderer;
    private TagRenderingBase _divRenderer;
    private TagRenderingBase _spanRenderer;

    private TreeRenderState _trs;

    private ImageTag.State _imgState = new ImageTag.State();
    private AnchorTag.State _anchorState = new AnchorTag.State();
    private DivTag.State _divState = new DivTag.State();
    private SpanTag.State _spanState = new SpanTag.State();

    private ServletContext _servletContext;
    private HttpServletRequest _req;
    private HttpServletResponse _res;

    TreeRenderer(TreeRenderState trs, HttpServletRequest request,
                 HttpServletResponse response, ServletContext servletContext)
    {
        _trs = trs;
        _imageRenderer = TagRenderingBase.Factory.getRendering(TagRenderingBase.IMAGE_TAG, request);
        _anchorRenderer = TagRenderingBase.Factory.getRendering(TagRenderingBase.ANCHOR_TAG, request);
        _divRenderer = TagRenderingBase.Factory.getRendering(TagRenderingBase.DIV_TAG, request);
        _spanRenderer = TagRenderingBase.Factory.getRendering(TagRenderingBase.SPAN_TAG, request);
        _servletContext = servletContext;
        _req = request;
        _res = response;
    }

    abstract protected void registerTagError(String message, Throwable e)
            throws JspException;

    abstract protected String renderTagId(HttpServletRequest request, String tagId, AbstractHtmlState state);

    protected void renderBeforeNode(AbstractRenderAppender writer, TreeElement node)
    {
    }

    protected void renderAfterNode(AbstractRenderAppender writer, TreeElement node)
    {
    }

    /**
     * This is a recursive method which generates the markup for the tree.
     * @param sb
     * @param node
     * @param level
     * @param attrs
     * @param state
     * @throws javax.servlet.jsp.JspException
     */
    protected void render(InternalStringBuilder sb, TreeElement node, int level, AttributeRenderer attrs,
                          InheritableState state)
            throws JspException
    {
        // assert the values...
        assert(sb != null);
        assert(node != null);

        String encoding = _res.getCharacterEncoding();
        String nodeName = node.getName();

        assert(nodeName != null);

        // HACK to take into account special characters like = and &
        // in the node name, could remove this code if encode URL
        // and later request.getParameter() could deal with = and &
        // character in parameter values.
        String encodedNodeName = null;
        try {
            encodedNodeName = URLCodec.encode(nodeName, encoding);
            assert(encodedNodeName != null);
        }
        catch (IOException e) {
            // report the exception and return.
            String s = Bundle.getString("Tags_TreeEncodingError", null);
            registerTagError(s, e);
            return;
        }

        // add any attributes to the renderer
        AttributeRenderer.RemoveInfo removes = attrs.addElement(node);

        // get the renderers for this tree...
        InternalStringBuilder img = new InternalStringBuilder(32);


        // Render the beginning of this node
        _divState.clear();
        String tagId = node.getTagId();
        String script = null;
        if (tagId != null) {
            script = renderTagId(_req, tagId, _divState);
        }
        attrs.renderDiv(_divState, node);
        if (_trs.runAtClient) {
            _divState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_LEVEL, Integer.toString(level));
        }
        sb.append("   ");
        StringBuilderRenderAppender writer = new StringBuilderRenderAppender(sb);
        renderBeforeNode(writer, node);
        _divRenderer.doStartTag(writer, _divState);
        sb.append("\n");
        if (script != null)
            sb.append(script);

        // In devMode we will verify the structure of the tree.  This will not run in
        // production mode.
        ServletContext servletContext = InternalUtils.getServletContext(_req);
        boolean devMode = !AdapterManager.getServletContainerAdapter(servletContext).isInProductionMode();
        if (devMode) {
            boolean error = false;
            InternalStringBuilder errorText = new InternalStringBuilder(64);
            if (node.getName() == null) {
                errorText.append("name");
                error = true;
            }
            if (node.getParent() == null) {
                if (error)
                    errorText.append(", ");
                errorText.append("parent");
            }

            if (error)
                registerTagError(Bundle.getString("Tags_TreeStructureError", errorText.toString()), null);
        }

        // check for tree override properties, the second
        // case here is because the root runs through this an by definitions
        // has InheritableState == state
        InheritableState is = node.getInheritableState();
        if (is != null && is != state) {
            is.setParent(state);
            state = is;
        }

        // Create the appropriate number of indents
        // These are either the spacer.gif if the parent is the last in the line or the
        // vertical line gif if the parent is not the last child.
        _imgState.clear();
        _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, WIDTH, "16px");
        _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, BORDER, "0");
        _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, ALT, "", false);
        for (int i = 0; i < level; i++) {
            int levels = level - i;
            TreeElement parent = node;
            for (int j = 1; j <= levels; j++)
                parent = parent.getParent();

            img.setLength(0);
            img.append(state.getImageRoot());
            img.append('/');
            if (parent.isLast()) {
                img.append(state.getImageSpacer());
                _imgState.style = null;
            }
            else {
                img.append(state.getVerticalLineImage());
                _imgState.style = "vertical-align:bottom;";
            }
            sb.append("      ");
            _imgState.src = img.toString();
            _imageRenderer.doStartTag(writer, _imgState);
            _imageRenderer.doEndTag(writer);
            sb.append("\n");
        }

        // boolean flag that will indicate if there is an open anchor created
        boolean closeAnchor = false;
        if (!_trs.runAtClient)
            closeAnchor = renderExpansionAnchor(sb, _anchorRenderer, node, nodeName, state);
        else {
            // Render client expansion and initialize the tree JavaScript support
            closeAnchor = renderClientExpansionAnchor(sb, _anchorRenderer, node, encodedNodeName, state);
        }

        // place the image into the anchor....
        // The type of the image depends upon the position and the type of the node.
        String alt = "";
        img.setLength(0);
        img.append(state.getImageRoot());
        img.append('/');
        if (node.isLeaf()) {                        // leaf node either last or middle
            if (node.isLast())
                img.append(state.getLastLineJoinImage());
            else
                img.append(state.getLineJoinImage());
        }
        else if (node.isExpanded()) {               // interior node that is expanded
            alt = Bundle.getString("Tags_TreeAltTextCollapse", null);
            String rImg = null;
            if (node.getParent() == null && node instanceof ITreeRootElement) {
                rImg = ((ITreeRootElement) node).getRootNodeExpandedImage();
                if (rImg != null) {
                    img.append(rImg);
                }
            }
            if (rImg == null) {
                if (node.isLast())
                    img.append(state.getLastNodeExpandedImage());
                else
                    img.append(state.getNodeExpandedImage());
            }
        }
        else {                                      // interior not expanded
            alt = Bundle.getString("Tags_TreeAltTextExpand", null);
            String rImg = null;
            if (node.getParent() == null && node instanceof ITreeRootElement) {
                rImg = ((ITreeRootElement) node).getRootNodeCollapsedImage();
                if (rImg != null) {
                    img.append(rImg);
                }
            }
            if (rImg == null) {
                if (node.isLast())
                    img.append(state.getLastNodeCollapsedImage());
                else
                    img.append(state.getNodeCollapsedImage());
            }
        }

        // write out the image which occurs next to the icon
        if (!closeAnchor)
            sb.append("      ");

        _imgState.clear();
        _imgState.src = img.toString();
        _imgState.style = "vertical-align:bottom;";
        _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, BORDER, "0");
        _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, ALT, alt, false);

        _imageRenderer.doStartTag(writer, _imgState);
        _imageRenderer.doEndTag(writer);

        // close the anchor if one was openned...
        if (closeAnchor)
            _anchorRenderer.doEndTag(writer);
        sb.append("\n");

        // Calculate the selection link for this node, if the node is disabled, we can skip
        // this because a disabled node may not be selected.
        String selectionLink = null;
        if (!node.isDisabled()) {

            // The action on the node overrides all.  Otherwise, check to see if there
            // is either an href or clientAction.  If neither is set, then we inherit the
            // action defined on the trees inheritable state.
            String action = node.getAction();
            if (action == null) {
                selectionLink = node.getHref();
                if (selectionLink == null && node.getClientAction() != null)
                    selectionLink = "";
                if (selectionLink == null)
                    action = state.getSelectionAction();
            }

            // create the selection link
            if (action != null && selectionLink == null) {

                HashMap params = null;
                boolean remove = false;
                params = node.getParams();
                if (params == null) {
                    params = new HashMap();
                    remove = true;
                }
                params.put(TreeElement.SELECTED_NODE, nodeName);
                if (_trs.tagId != null) {
                    params.put(TreeElement.TREE_ID, _trs.tagId);
                }

                // Add the jpf ScopeID param if necessary.
                String scope = node.getScope();
                if (scope != null) {
                    params.put(ScopedServletUtils.SCOPE_ID_PARAM, scope);
                }

                String uri = null;
                try {
                    boolean xml = TagRenderingBase.Factory.isXHTML(_req);
                    uri = PageFlowUtils.getRewrittenActionURI(_servletContext, _req, _res, action, params, null, xml);
                }
                catch (URISyntaxException e) {
                    // report the error...
                    String s = Bundle.getString("Tags_Tree_Node_URLException",
                            new Object[]{action, e.getMessage()});
                    registerTagError(s, e);
                }

                if (remove) {
                    params.remove(TreeElement.SELECTED_NODE);
                    if (_trs.tagId != null) {
                        params.remove(TreeElement.TREE_ID);
                    }

                    if (scope != null) {
                        params.remove(ScopedServletUtils.SCOPE_ID_PARAM);
                    }
                }

                if (uri != null) {
                    selectionLink = _res.encodeURL(uri);
                }
            }
        }

        TagRenderingBase endRender = null;
        // if there is a selection link we need to put an anchor out.
        if (selectionLink != null) {
            _anchorState.clear();
            _anchorState.href = selectionLink;
            String target = node.getTarget();
            if (target == null) {
                target = state.getSelectionTarget();
            }
            _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TARGET, target);
            String title = node.getTitle();
            _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TITLE, title);

            // set the selection styles
            if (node.isSelected()) {
                _anchorState.style = _trs.selectedStyle;
                _anchorState.styleClass = _trs.selectedStyleClass;
            }
            else {
                _anchorState.style = _trs.unselectedStyle;
                _anchorState.styleClass = _trs.unselectedStyleClass;
            }
            if (_anchorState.style == null && _anchorState.styleClass == null) {
                _anchorState.style = "text-decoration: none";
            }

            // render any attributes applied to the HTML
            attrs.renderSelectionLink(_anchorState, node);

            // render the runAtClient attributes
            if (_trs.runAtClient) {
                String action = node.getClientAction();
                if (action != null) {
                    action = HtmlUtils.escapeEscapes(action);
                    action = ScriptRequestState.getString("netuiAction", new Object[]{action});
                }
                _anchorState.registerAttribute(AbstractHtmlState.ATTR_JAVASCRIPT, ONCLICK, action);
                // Jira 299
                //_anchorState.onClick = action;
            }

            // actually render the anchor.
            sb.append("      ");
            _anchorRenderer.doStartTag(writer, _anchorState);
            endRender = _anchorRenderer;
        }
        else {
            // This node doesn's support selection.  This means we consider it disabled.  We will
            // put a span around it and set the style/class to indicate that it is disabled.
            _spanState.clear();
            _spanState.styleClass = _trs.disabledStyleClass;
            _spanState.style = _trs.disabledStyle;
            sb.append("      ");
            _spanRenderer.doStartTag(writer, _spanState);
            endRender = _spanRenderer;
        }
        sb.append("&nbsp;");

        // Render the icon for this node, there will always unless the tree turns off default
        // icons by setting the useDefaultIcons attribute to false.
        String icon = node.getIcon();
        if (icon == null) {
            icon = state.getIconRoot() + "/" + state.getItemIcon();
        }
        else {
            icon = state.getIconRoot() + "/" + icon;
        }

        // write out the icon
        String label = node.getLabel();
        if (icon != null) {
            _imgState.clear();
            _imgState.src = icon;
            _imgState.style = "vertical-align:text-top";
            alt = null;
            if (label != null && node.isLabelLegalAsAlt())
                alt = label;
            else
                alt = node.getTitle();
            if (alt == null)
                alt = Bundle.getString("Tags_TreeAltText", null);
            _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, ALT, alt, false);
            _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, BORDER, "0");

            // set the inheritted attributes
            attrs.renderIconImage(_imgState, node);
            _imageRenderer.doStartTag(writer, _imgState);
            _imageRenderer.doEndTag(writer);
            sb.append("&nbsp;");
        }

        // Render the label for this node (if any)
        if (label != null) {
            if (_trs.escapeContent) {
                InternalStringBuilder s = new InternalStringBuilder(label.length() + 16);
                StringBuilderRenderAppender sbAppend = new StringBuilderRenderAppender(sb);
                HtmlUtils.filter(label, sbAppend);
                label = s.toString();
            }
            sb.append(label);
            sb.append("&nbsp;");
        }
        endRender.doEndTag(writer);

        // if there is content then we should render that here...
        String ctnt = node.getContent();
        if (ctnt != null) {
            if (_trs.escapeContent) {
                InternalStringBuilder s = new InternalStringBuilder(ctnt.length() + 16);
                StringBuilderRenderAppender sbAppend = new StringBuilderRenderAppender(sb);
                HtmlUtils.filter(ctnt, sbAppend);
                ctnt = s.toString();
            }
            sb.append("\n      ");
            sb.append(ctnt);
        }

        // Render the end of this node
        sb.append("\n   ");
        _divRenderer.doEndTag(writer);
        sb.append("\n");
        renderAfterNode(writer, node);

        // now remove all of the attributes scoped with this...

        attrs.removeElementScoped(node, removes);
        // Render the children of this node
        // If the node is expanded we render it
        //    If we are runAtClient and the node is Not expandOnServer then render it
        if (node.isExpanded() || (_trs.runAtClient && !node.isExpandOnServer())) {
            TreeElement children[] = node.getChildren();
            int newLevel = level + 1;
            for (int i = 0; i < children.length; i++) {
                render(sb, children[i], newLevel, attrs, state);
            }
        }
        attrs.removeElement(node, removes);
    }

    private boolean renderExpansionAnchor(InternalStringBuilder sb, TagRenderingBase anchorRenderer,
                                          TreeElement node, String nodeName, InheritableState state)
            throws JspException
    {
        // Render the tree state image for this node
        String action = state.getExpansionAction();
        if (action == null) {
            action = state.getSelectionAction();
        }
        boolean isAction = PageFlowTagUtils.isAction(_req, action);
        if (!isAction) {
            registerTagError(Bundle.getString("Tags_BadAction", action), null);
            return false;
        }

        // encode the tree parameters into the action.
        HashMap params = new HashMap();
        params.put(TreeElement.EXPAND_NODE, nodeName);
        assert (_trs.tagId != null);
        params.put(TreeElement.TREE_ID, _trs.tagId);
        String uri = null;
        try {
            boolean xml = TagRenderingBase.Factory.isXHTML(_req);
            uri = PageFlowUtils.getRewrittenActionURI(_servletContext, _req, _res, action, params, null, xml);
        }
        catch (URISyntaxException e) {
            // report the error...
            String s = Bundle.getString("Tags_Tree_Node_URLException",
                    new Object[]{action, e.getMessage()});
            registerTagError(s, e);
        }

        boolean ret = false;
        if ((uri != null) && !node.isLeaf()) {
            _anchorState.clear();
            _anchorState.href = _res.encodeURL(uri);
            sb.append("      ");
            StringBuilderRenderAppender writer = new StringBuilderRenderAppender(sb);
            anchorRenderer.doStartTag(writer, _anchorState);
            ret = true;
        }
        return ret;
    }

    /**
     * @param sb
     * @param node
     * @param encodedNodeName
     * @return
     */
    private boolean renderClientExpansionAnchor(InternalStringBuilder sb, TagRenderingBase anchorRenderer,
                                                TreeElement node, String encodedNodeName,
                                                InheritableState state)
    {
        boolean imgOverride = (state != state.getParent() && state.getParent() != null) ||
                (node.getParent() == null);

        if (!node.isLeaf()) {
            boolean expanded = node.isExpanded();
            _anchorState.clear();
            _anchorState.href = "";
            _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_ANCHOR,
                    (expanded ? TreeElement.TREE_EXPAND_STATE : TreeElement.TREE_COLLAPSE_STATE));
            _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_ANCHOR_INIT, "true");
            _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_ANCHOR_ID, encodedNodeName);
            if (node.isLast()) {
                _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_NODE_LAST, "true");
            }

            // Does this node have it's images being overridden?
            if (imgOverride) {
                if (node.getParent() == null) {
                    String rootImg = ((ITreeRootElement) node).getRootNodeCollapsedImage();
                    if (rootImg != null)
                        _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_COLLAPSE_IMAGE,
                                state.getImageRoot() + "/" + rootImg);
                    else
                        _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_COLLAPSE_IMAGE,
                                state.getImageRoot() + "/" + state.getLastNodeCollapsedImage());
                    rootImg = ((ITreeRootElement) node).getRootNodeExpandedImage();
                    if (rootImg != null)
                        _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_IMAGE,
                                state.getImageRoot() + "/" + rootImg);
                    else
                        _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_IMAGE,
                                state.getImageRoot() + "/" + state.getLastNodeExpandedImage());
                }
                else if (node.isLast()) {
                    _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_COLLAPSE_IMAGE,
                            state.getImageRoot() + "/" + state.getLastNodeCollapsedImage());
                    _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_IMAGE,
                            state.getImageRoot() + "/" + state.getLastNodeExpandedImage());
                }
                else {
                    _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_COLLAPSE_IMAGE,
                            state.getImageRoot() + "/" + state.getNodeCollapsedImage());
                    _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_IMAGE,
                            state.getImageRoot() + "/" + state.getNodeExpandedImage());
                }
            }

            if (node.isExpandOnServer() && !node.isExpanded()) {
                String path = _req.getServletPath();
                int idx = path.lastIndexOf('/');
                if (idx != -1) {
                    path = path.substring(1, idx);
                }
                _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND, "true");
                _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_PATH, path);
            }

            sb.append("      ");
            StringBuilderRenderAppender writer = new StringBuilderRenderAppender(sb);
            anchorRenderer.doStartTag(writer, _anchorState);
            return true;
        }
        return false;
    }
}
TOP

Related Classes of org.apache.beehive.netui.tags.tree.TreeRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.