0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: *
0017: * $Header:$
0018: */
0019: package org.apache.beehive.netui.tags.tree;
0020:
0021: import org.apache.beehive.netui.util.internal.InternalStringBuilder;
0022:
0023: import org.apache.beehive.netui.core.URLCodec;
0024: import org.apache.beehive.netui.pageflow.PageFlowUtils;
0025: import org.apache.beehive.netui.pageflow.internal.AdapterManager;
0026: import org.apache.beehive.netui.pageflow.internal.InternalUtils;
0027: import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils;
0028: import org.apache.beehive.netui.tags.HtmlUtils;
0029: import org.apache.beehive.netui.tags.html.HtmlConstants;
0030: import org.apache.beehive.netui.tags.internal.PageFlowTagUtils;
0031: import org.apache.beehive.netui.tags.javascript.ScriptRequestState;
0032: import org.apache.beehive.netui.tags.rendering.*;
0033: import org.apache.beehive.netui.util.Bundle;
0034:
0035: import javax.servlet.ServletContext;
0036: import javax.servlet.http.HttpServletRequest;
0037: import javax.servlet.http.HttpServletResponse;
0038: import javax.servlet.jsp.JspException;
0039: import java.io.IOException;
0040: import java.net.URISyntaxException;
0041: import java.util.HashMap;
0042:
0043: /**
0044: * This class renders the HTML markup for the NetUI Tree. The {@link #render} method
0045: * recursively renders child nodes of the tree if they're expanded.
0046: *
0047: * <p>
0048: * By default, this predefined NetUI implementation is the class used
0049: * across the Web application. NetUI may be configured to use a different tree
0050: * renderer implementation as the renderer in the Web application. A custom
0051: * tree renderer is configured by setting the <tree-renderer-class> element
0052: * in the beehive-netui-config.xml file with the name of a class that extends
0053: * this class.
0054: * </p>
0055: * <code>
0056: * <tree-renderer-class>com.xyz.tree.CustomTreeRenderer</tree-renderer-class>
0057: * </code>
0058: */
0059: public class TreeRenderer implements HtmlConstants {
0060: protected static final String FORMAT_INDENT = " ";
0061: protected static final String FORMAT_NBSP = " ";
0062: protected static final String FORMAT_NEWLINE = "\n";
0063: protected static final String FORMAT_SHORT_INDENT = " ";
0064: protected static final String FORMAT_NEWLINE_INDENT = FORMAT_NEWLINE
0065: + FORMAT_INDENT;
0066: protected static final String FORMAT_NEWLINE_SHORT_INDENT = FORMAT_NEWLINE
0067: + FORMAT_SHORT_INDENT;
0068:
0069: private TagRenderingBase _imageRenderer;
0070: private TagRenderingBase _anchorRenderer;
0071: private TagRenderingBase _divRenderer;
0072: private TagRenderingBase _spanRenderer;
0073:
0074: private ImageTag.State _imgState = new ImageTag.State();
0075: private AnchorTag.State _anchorState = new AnchorTag.State();
0076: private DivTag.State _divState = new DivTag.State();
0077: private SpanTag.State _spanState = new SpanTag.State();
0078:
0079: protected TreeRenderState _trs;
0080:
0081: protected ServletContext _servletContext;
0082: protected HttpServletRequest _req;
0083: protected HttpServletResponse _res;
0084:
0085: private TreeRenderSupport _treeRenderSupport;
0086:
0087: public void init(TreeRenderState trs, HttpServletRequest request,
0088: HttpServletResponse response, ServletContext servletContext) {
0089: _trs = trs;
0090: _imageRenderer = TagRenderingBase.Factory.getRendering(
0091: TagRenderingBase.IMAGE_TAG, request);
0092: _anchorRenderer = TagRenderingBase.Factory.getRendering(
0093: TagRenderingBase.ANCHOR_TAG, request);
0094: _divRenderer = TagRenderingBase.Factory.getRendering(
0095: TagRenderingBase.DIV_TAG, request);
0096: _spanRenderer = TagRenderingBase.Factory.getRendering(
0097: TagRenderingBase.SPAN_TAG, request);
0098: _servletContext = servletContext;
0099: _req = request;
0100: _res = response;
0101: }
0102:
0103: /**
0104: * This method is set by the NetUI internals to defined an object that
0105: * handles issues specific to rendering a tree for certain paths of
0106: * execution in NetUI.
0107: * @param treeRenderSupport the class to handle NetUI specific issues while
0108: * rendering the tree.
0109: */
0110: protected void setTreeRenderSupport(
0111: TreeRenderSupport treeRenderSupport) {
0112: _treeRenderSupport = treeRenderSupport;
0113: }
0114:
0115: protected TreeRenderSupport getTreeRenderSupport() {
0116: return _treeRenderSupport;
0117: }
0118:
0119: protected void registerTagError(String message, Throwable e)
0120: throws JspException {
0121: _treeRenderSupport.registerTagError(message, e);
0122: }
0123:
0124: protected String renderTagId(HttpServletRequest request,
0125: String tagId, AbstractHtmlState state) {
0126: return _treeRenderSupport.renderTagId(request, tagId, state);
0127: }
0128:
0129: protected void renderBeforeNode(AbstractRenderAppender writer,
0130: TreeElement node) {
0131: _treeRenderSupport.renderBeforeNode(writer, node);
0132: }
0133:
0134: protected void renderAfterNode(AbstractRenderAppender writer,
0135: TreeElement node) {
0136: _treeRenderSupport.renderAfterNode(writer, node);
0137: }
0138:
0139: /**
0140: * This is a recursive method which generates the markup for the tree.
0141: * @param writer the appender where the tree markup is appended
0142: * @param node the node to render
0143: * @param level the level or depth of the node within the tree
0144: * @param attrs renderer for supported attributes
0145: * @param state the set of tree properties that are used to render the tree markup
0146: * @throws javax.servlet.jsp.JspException
0147: */
0148: public void render(AbstractRenderAppender writer, TreeElement node,
0149: int level, AttributeRenderer attrs, InheritableState state)
0150: throws JspException {
0151: // assert the values...
0152: assert (writer != null);
0153: assert (node != null);
0154:
0155: String nodeName = node.getName();
0156: assert (nodeName != null);
0157:
0158: // add any attributes to the renderer
0159: AttributeRenderer.RemoveInfo removes = attrs.addElement(node);
0160:
0161: // Render the beginning of this node
0162: _divState.clear();
0163: String tagId = node.getTagId();
0164: String script = null;
0165: if (tagId != null) {
0166: script = renderTagId(_req, tagId, _divState);
0167: }
0168: attrs.renderDiv(_divState, node);
0169: if (_trs.runAtClient) {
0170: _divState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0171: TreeElement.TREE_LEVEL, Integer.toString(level));
0172: }
0173: renderStartDivPrefix(writer, node);
0174: renderBeforeNode(writer, node);
0175: _divRenderer.doStartTag(writer, _divState);
0176: renderStartDivSuffix(writer, node);
0177: if (script != null)
0178: writer.append(script);
0179:
0180: // In devMode we will verify the structure of the tree. This will not run in
0181: // production mode.
0182: ServletContext servletContext = InternalUtils
0183: .getServletContext(_req);
0184: boolean devMode = !AdapterManager.getServletContainerAdapter(
0185: servletContext).isInProductionMode();
0186: if (devMode) {
0187: boolean error = false;
0188: InternalStringBuilder errorText = new InternalStringBuilder(
0189: 64);
0190: if (node.getName() == null) {
0191: errorText.append("name");
0192: error = true;
0193: }
0194: if (node.getParent() == null) {
0195: if (error)
0196: errorText.append(", ");
0197: errorText.append("parent");
0198: }
0199:
0200: if (error)
0201: registerTagError(Bundle
0202: .getString("Tags_TreeStructureError", errorText
0203: .toString()), null);
0204: }
0205:
0206: // check for tree override properties, the second
0207: // case here is because the root runs through this an by definitions
0208: // has InheritableState == state
0209: InheritableState is = node.getInheritableState();
0210: if (is != null && is != state) {
0211: is.setParent(state);
0212: state = is;
0213: }
0214:
0215: // write out the images that create the leading indentation for the given node
0216: renderIndentation(writer, node, level, state);
0217:
0218: // write out the image which occurs next to the node icon
0219: renderConnectionImage(writer, node, nodeName, state);
0220:
0221: // If needed, render the selection link around the icon for this node.
0222: // Note that the tag rendered here needs to be closed after the actual
0223: // icon and label are rendered.
0224: TagRenderingBase endRender = renderSelectionLink(writer, node,
0225: nodeName, attrs, state);
0226:
0227: // render the actual icon for this node
0228: renderItemIcon(writer, node, attrs, state);
0229:
0230: // render the label for this node (if any)
0231: renderLabel(writer, node);
0232:
0233: // now, close the selection link (or span) tag
0234: if (endRender != null) {
0235: endRender.doEndTag(writer);
0236: }
0237: renderSelectionLinkSuffix(writer, node);
0238:
0239: // if there is content then we should render that here...
0240: renderContent(writer, node);
0241:
0242: // render the end of this node
0243: renderEndDivPrefix(writer, node);
0244: _divRenderer.doEndTag(writer);
0245: renderEndDivPrefix(writer, node);
0246: renderAfterNode(writer, node);
0247:
0248: // now remove all of the attributes scoped with this...
0249: attrs.removeElementScoped(node, removes);
0250:
0251: // Render the children of this node
0252: // If the node is expanded we render it
0253: // If we are runAtClient and the node is Not expandOnServer then render it
0254: if (node.isExpanded()
0255: || (_trs.runAtClient && !node.isExpandOnServer())) {
0256: TreeElement children[] = node.getChildren();
0257: int newLevel = level + 1;
0258: for (int i = 0; i < children.length; i++) {
0259: render(writer, children[i], newLevel, attrs, state);
0260: }
0261: }
0262: attrs.removeElement(node, removes);
0263: }
0264:
0265: /**
0266: * Write out the images that create the leading indentation for the given node.
0267: * @param writer the appender where the node indentation images are appended
0268: * @param node the node to render
0269: * @param level the level or depth of the node within the tree
0270: * @param state the set of tree properties that are used to render the tree markup
0271: */
0272: protected void renderIndentation(AbstractRenderAppender writer,
0273: TreeElement node, int level, InheritableState state) {
0274: InternalStringBuilder img = new InternalStringBuilder(32);
0275:
0276: // Create the appropriate number of indents
0277: // These are either the spacer.gif if the parent is the last in the line or the
0278: // vertical line gif if the parent is not the last child.
0279: _imgState.clear();
0280: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0281: WIDTH, "16px");
0282: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0283: BORDER, "0");
0284: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0285: ALT, "", false);
0286: for (int i = 0; i < level; i++) {
0287: int levels = level - i;
0288: TreeElement parent = node;
0289: for (int j = 1; j <= levels; j++) {
0290: parent = parent.getParent();
0291: }
0292:
0293: img.setLength(0);
0294: img.append(state.getImageRoot());
0295: img.append('/');
0296: if (parent.isLast()) {
0297: renderSpacerPrefix(writer, node);
0298: img.append(state.getImageSpacer());
0299: _imgState.style = null;
0300: } else {
0301: renderVerticalLinePrefix(writer, node);
0302: img.append(state.getVerticalLineImage());
0303: _imgState.style = "vertical-align:bottom;";
0304: }
0305: _imgState.src = img.toString();
0306: _imageRenderer.doStartTag(writer, _imgState);
0307: _imageRenderer.doEndTag(writer);
0308: if (parent.isLast()) {
0309: renderSpacerSuffix(writer, node);
0310: } else {
0311: renderVerticalLineSuffix(writer, node);
0312: }
0313: }
0314: }
0315:
0316: /**
0317: * Write out the image which occurs next to the node icon. This is
0318: * usually some kind of connecting line, expand, or collapse image.
0319: * @param writer the appender where the tree markup is appended
0320: * @param node the node to render
0321: * @param nodeName the unique name of the node
0322: * @param state the set of tree properties that are used to render the tree markup
0323: * @throws JspException
0324: */
0325: protected void renderConnectionImage(AbstractRenderAppender writer,
0326: TreeElement node, String nodeName, InheritableState state)
0327: throws JspException {
0328: InternalStringBuilder img = new InternalStringBuilder(32);
0329:
0330: // HACK to take into account special characters like = and &
0331: // in the node name, could remove this code if encode URL
0332: // and later request.getParameter() could deal with = and &
0333: // character in parameter values.
0334: String encodedNodeName = null;
0335: try {
0336: encodedNodeName = URLCodec.encode(nodeName, _res
0337: .getCharacterEncoding());
0338: assert (encodedNodeName != null);
0339: } catch (IOException e) {
0340: // report the exception and return.
0341: String s = Bundle.getString("Tags_TreeEncodingError", null);
0342: registerTagError(s, e);
0343: return;
0344: }
0345:
0346: // boolean flag that will indicate if there is an open anchor created
0347: boolean closeAnchor = false;
0348: if (!_trs.runAtClient) {
0349: closeAnchor = renderExpansionAnchor(writer,
0350: _anchorRenderer, node, nodeName, state);
0351: } else {
0352: // Render client expansion and initialize the tree JavaScript support
0353: closeAnchor = renderClientExpansionAnchor(writer,
0354: _anchorRenderer, node, encodedNodeName, state);
0355: }
0356:
0357: // place the image into the anchor....
0358: // The type of the image depends upon the position and the type of the node.
0359: String alt = "";
0360: img.setLength(0);
0361: img.append(state.getImageRoot());
0362: img.append('/');
0363: if (node.isLeaf()) { // leaf node either last or middle
0364: if (node.isLast())
0365: img.append(state.getLastLineJoinImage());
0366: else
0367: img.append(state.getLineJoinImage());
0368: } else if (node.isExpanded()) { // interior node that is expanded
0369: alt = Bundle.getString("Tags_TreeAltTextCollapse", null);
0370: String rImg = null;
0371: if (node.getParent() == null
0372: && node instanceof ITreeRootElement) {
0373: rImg = ((ITreeRootElement) node)
0374: .getRootNodeExpandedImage();
0375: if (rImg != null) {
0376: img.append(rImg);
0377: }
0378: }
0379: if (rImg == null) {
0380: if (node.isLast())
0381: img.append(state.getLastNodeExpandedImage());
0382: else
0383: img.append(state.getNodeExpandedImage());
0384: }
0385: } else { // interior not expanded
0386: alt = Bundle.getString("Tags_TreeAltTextExpand", null);
0387: String rImg = null;
0388: if (node.getParent() == null
0389: && node instanceof ITreeRootElement) {
0390: rImg = ((ITreeRootElement) node)
0391: .getRootNodeCollapsedImage();
0392: if (rImg != null) {
0393: img.append(rImg);
0394: }
0395: }
0396: if (rImg == null) {
0397: if (node.isLast())
0398: img.append(state.getLastNodeCollapsedImage());
0399: else
0400: img.append(state.getNodeCollapsedImage());
0401: }
0402: }
0403:
0404: if (!closeAnchor) {
0405: renderConnectionImagePrefix(writer, node);
0406: }
0407:
0408: // write out the image which occurs next to the icon
0409: _imgState.clear();
0410: _imgState.src = img.toString();
0411: _imgState.style = "vertical-align:bottom;";
0412: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0413: BORDER, "0");
0414: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0415: ALT, alt, false);
0416:
0417: _imageRenderer.doStartTag(writer, _imgState);
0418: _imageRenderer.doEndTag(writer);
0419:
0420: // close the anchor if one was openned...
0421: if (closeAnchor) {
0422: _anchorRenderer.doEndTag(writer);
0423: }
0424: renderConnectionImageSuffix(writer, node);
0425: }
0426:
0427: private boolean renderExpansionAnchor(
0428: AbstractRenderAppender writer,
0429: TagRenderingBase anchorRenderer, TreeElement node,
0430: String nodeName, InheritableState state)
0431: throws JspException {
0432: // Render the tree state image for this node
0433: String action = state.getExpansionAction();
0434: if (action == null) {
0435: action = state.getSelectionAction();
0436: }
0437: boolean isAction = PageFlowTagUtils.isAction(_req, action);
0438: if (!isAction) {
0439: registerTagError(
0440: Bundle.getString("Tags_BadAction", action), null);
0441: return false;
0442: }
0443:
0444: // encode the tree parameters into the action.
0445: HashMap params = new HashMap();
0446: params.put(TreeElement.EXPAND_NODE, nodeName);
0447: assert (_trs.tagId != null);
0448: params.put(TreeElement.TREE_ID, _trs.tagId);
0449: String uri = null;
0450: try {
0451: boolean xml = TagRenderingBase.Factory.isXHTML(_req);
0452: uri = PageFlowUtils.getRewrittenActionURI(_servletContext,
0453: _req, _res, action, params, null, xml);
0454: } catch (URISyntaxException e) {
0455: // report the error...
0456: String s = Bundle.getString("Tags_Tree_Node_URLException",
0457: new Object[] { action, e.getMessage() });
0458: registerTagError(s, e);
0459: }
0460:
0461: boolean ret = false;
0462: if ((uri != null) && !node.isLeaf()) {
0463: _anchorState.clear();
0464: _anchorState.href = _res.encodeURL(uri);
0465: renderConnectionImagePrefix(writer, node);
0466: anchorRenderer.doStartTag(writer, _anchorState);
0467: ret = true;
0468: }
0469: return ret;
0470: }
0471:
0472: private boolean renderClientExpansionAnchor(
0473: AbstractRenderAppender writer,
0474: TagRenderingBase anchorRenderer, TreeElement node,
0475: String encodedNodeName, InheritableState state) {
0476: boolean imgOverride = (state != state.getParent() && state
0477: .getParent() != null)
0478: || (node.getParent() == null);
0479:
0480: if (!node.isLeaf()) {
0481: boolean expanded = node.isExpanded();
0482: _anchorState.clear();
0483: _anchorState.href = "#";
0484: _anchorState.registerAttribute(
0485: AbstractHtmlState.ATTR_GENERAL,
0486: TreeElement.TREE_ANCHOR,
0487: (expanded ? TreeElement.TREE_EXPAND_STATE
0488: : TreeElement.TREE_COLLAPSE_STATE));
0489: _anchorState.registerAttribute(
0490: AbstractHtmlState.ATTR_GENERAL,
0491: TreeElement.TREE_ANCHOR_INIT, "true");
0492: _anchorState.registerAttribute(
0493: AbstractHtmlState.ATTR_GENERAL,
0494: TreeElement.TREE_ANCHOR_ID, encodedNodeName);
0495: if (node.isLast()) {
0496: _anchorState.registerAttribute(
0497: AbstractHtmlState.ATTR_GENERAL,
0498: TreeElement.TREE_NODE_LAST, "true");
0499: }
0500:
0501: // Does this node have it's images being overridden?
0502: if (imgOverride) {
0503: if (node.getParent() == null) {
0504: String rootImg = ((ITreeRootElement) node)
0505: .getRootNodeCollapsedImage();
0506: if (rootImg != null)
0507: _anchorState.registerAttribute(
0508: AbstractHtmlState.ATTR_GENERAL,
0509: TreeElement.TREE_COLLAPSE_IMAGE, state
0510: .getImageRoot()
0511: + "/" + rootImg);
0512: else
0513: _anchorState
0514: .registerAttribute(
0515: AbstractHtmlState.ATTR_GENERAL,
0516: TreeElement.TREE_COLLAPSE_IMAGE,
0517: state.getImageRoot()
0518: + "/"
0519: + state
0520: .getLastNodeCollapsedImage());
0521: rootImg = ((ITreeRootElement) node)
0522: .getRootNodeExpandedImage();
0523: if (rootImg != null)
0524: _anchorState.registerAttribute(
0525: AbstractHtmlState.ATTR_GENERAL,
0526: TreeElement.TREE_EXPAND_IMAGE, state
0527: .getImageRoot()
0528: + "/" + rootImg);
0529: else
0530: _anchorState
0531: .registerAttribute(
0532: AbstractHtmlState.ATTR_GENERAL,
0533: TreeElement.TREE_EXPAND_IMAGE,
0534: state.getImageRoot()
0535: + "/"
0536: + state
0537: .getLastNodeExpandedImage());
0538: } else if (node.isLast()) {
0539: _anchorState
0540: .registerAttribute(
0541: AbstractHtmlState.ATTR_GENERAL,
0542: TreeElement.TREE_COLLAPSE_IMAGE,
0543: state.getImageRoot()
0544: + "/"
0545: + state
0546: .getLastNodeCollapsedImage());
0547: _anchorState.registerAttribute(
0548: AbstractHtmlState.ATTR_GENERAL,
0549: TreeElement.TREE_EXPAND_IMAGE, state
0550: .getImageRoot()
0551: + "/"
0552: + state.getLastNodeExpandedImage());
0553: } else {
0554: _anchorState.registerAttribute(
0555: AbstractHtmlState.ATTR_GENERAL,
0556: TreeElement.TREE_COLLAPSE_IMAGE, state
0557: .getImageRoot()
0558: + "/"
0559: + state.getNodeCollapsedImage());
0560: _anchorState.registerAttribute(
0561: AbstractHtmlState.ATTR_GENERAL,
0562: TreeElement.TREE_EXPAND_IMAGE, state
0563: .getImageRoot()
0564: + "/"
0565: + state.getNodeExpandedImage());
0566: }
0567: }
0568:
0569: if (node.isExpandOnServer() && !node.isExpanded()) {
0570: String path = _req.getServletPath();
0571: int idx = path.lastIndexOf('/');
0572: if (idx != -1) {
0573: path = path.substring(1, idx);
0574: }
0575: _anchorState.registerAttribute(
0576: AbstractHtmlState.ATTR_GENERAL,
0577: TreeElement.TREE_EXPAND, "true");
0578: _anchorState.registerAttribute(
0579: AbstractHtmlState.ATTR_GENERAL,
0580: TreeElement.TREE_EXPAND_PATH, path);
0581: }
0582:
0583: renderConnectionImagePrefix(writer, node);
0584: anchorRenderer.doStartTag(writer, _anchorState);
0585: return true;
0586: }
0587: return false;
0588: }
0589:
0590: /**
0591: * If needed, render the selection link around the icon for this node.
0592: * Note that the tag rendered here needs to be closed after the actual
0593: * icon and label are rendered.
0594: * @param writer the appender where the tree markup is appended
0595: * @param node the node to render
0596: * @param nodeName the unique name of the node
0597: * @param attrs renderer for supported attributes
0598: * @param state the set of tree properties that are used to render the tree markup
0599: * @return the selection link (or span) tag renderer to close after the item
0600: * icon and label rendered
0601: * @throws JspException
0602: */
0603: protected TagRenderingBase renderSelectionLink(
0604: AbstractRenderAppender writer, TreeElement node,
0605: String nodeName, AttributeRenderer attrs,
0606: InheritableState state) throws JspException {
0607: // calculate the selection link for this node
0608: String selectionLink = getSelectionlink(node, nodeName, state);
0609:
0610: TagRenderingBase endRender = null;
0611: // if there is a selection link we need to put an anchor out.
0612: if (selectionLink != null) {
0613: _anchorState.clear();
0614: _anchorState.href = selectionLink;
0615: String target = node.getTarget();
0616: if (target == null) {
0617: target = state.getSelectionTarget();
0618: }
0619: _anchorState.registerAttribute(
0620: AbstractHtmlState.ATTR_GENERAL, TARGET, target);
0621: String title = node.getTitle();
0622: _anchorState.registerAttribute(
0623: AbstractHtmlState.ATTR_GENERAL, TITLE, title);
0624:
0625: // set the selection styles
0626: if (node.isSelected()) {
0627: _anchorState.style = _trs.selectedStyle;
0628: _anchorState.styleClass = _trs.selectedStyleClass;
0629: } else {
0630: _anchorState.style = _trs.unselectedStyle;
0631: _anchorState.styleClass = _trs.unselectedStyleClass;
0632: }
0633: if (_anchorState.style == null
0634: && _anchorState.styleClass == null) {
0635: _anchorState.style = "text-decoration: none";
0636: }
0637:
0638: // render any attributes applied to the HTML
0639: attrs.renderSelectionLink(_anchorState, node);
0640:
0641: // render the runAtClient attributes
0642: if (_trs.runAtClient) {
0643: String action = node.getClientAction();
0644: if (action != null) {
0645: action = HtmlUtils.escapeEscapes(action);
0646: action = ScriptRequestState.getString(
0647: "netuiAction", new Object[] { action });
0648: }
0649: _anchorState.registerAttribute(
0650: AbstractHtmlState.ATTR_JAVASCRIPT, ONCLICK,
0651: action);
0652: // Jira 299
0653: //_anchorState.onClick = action;
0654: }
0655:
0656: // actually render the anchor.
0657: renderSelectionLinkPrefix(writer, node);
0658: _anchorRenderer.doStartTag(writer, _anchorState);
0659: endRender = _anchorRenderer;
0660: } else {
0661: // This node doesn's support selection. This means we consider it disabled. We will
0662: // put a span around it and set the style/class to indicate that it is disabled.
0663: _spanState.clear();
0664: _spanState.styleClass = _trs.disabledStyleClass;
0665: _spanState.style = _trs.disabledStyle;
0666: renderSelectionLinkPrefix(writer, node);
0667: _spanRenderer.doStartTag(writer, _spanState);
0668: endRender = _spanRenderer;
0669: }
0670:
0671: return endRender;
0672: }
0673:
0674: /**
0675: * Calculate the selection link for this node, if the node is disabled, we can skip
0676: * this because a disabled node may not be selected.
0677: * @param node the node to render
0678: * @param nodeName the unique name of the node
0679: * @param state the set of tree properties that are used to render the tree markup
0680: * @return the URL for the selection link
0681: * @throws JspException
0682: */
0683: protected String getSelectionlink(TreeElement node,
0684: String nodeName, InheritableState state)
0685: throws JspException {
0686: String selectionLink = null;
0687:
0688: if (!node.isDisabled()) {
0689:
0690: // The action on the node overrides all. Otherwise, check to see if there
0691: // is either an href or clientAction. If neither is set, then we inherit the
0692: // action defined on the trees inheritable state.
0693: String action = node.getAction();
0694: if (action == null) {
0695: selectionLink = node.getHref();
0696: if (selectionLink == null
0697: && node.getClientAction() != null) {
0698: selectionLink = "";
0699: }
0700: if (selectionLink == null) {
0701: action = state.getSelectionAction();
0702: }
0703: }
0704:
0705: // create the selection link
0706: if (action != null && selectionLink == null) {
0707: HashMap params = null;
0708: boolean remove = false;
0709: params = node.getParams();
0710: if (params == null) {
0711: params = new HashMap();
0712: remove = true;
0713: }
0714: params.put(TreeElement.SELECTED_NODE, nodeName);
0715: if (_trs.tagId != null) {
0716: params.put(TreeElement.TREE_ID, _trs.tagId);
0717: }
0718:
0719: // Add the jpf ScopeID param if necessary.
0720: String scope = node.getScope();
0721: if (scope != null) {
0722: params
0723: .put(ScopedServletUtils.SCOPE_ID_PARAM,
0724: scope);
0725: }
0726:
0727: String uri = null;
0728: try {
0729: boolean xml = TagRenderingBase.Factory
0730: .isXHTML(_req);
0731: uri = PageFlowUtils.getRewrittenActionURI(
0732: _servletContext, _req, _res, action,
0733: params, null, xml);
0734: } catch (URISyntaxException e) {
0735: // report the error...
0736: String s = Bundle.getString(
0737: "Tags_Tree_Node_URLException",
0738: new Object[] { action, e.getMessage() });
0739: registerTagError(s, e);
0740: }
0741:
0742: if (remove) {
0743: params.remove(TreeElement.SELECTED_NODE);
0744: if (_trs.tagId != null) {
0745: params.remove(TreeElement.TREE_ID);
0746: }
0747:
0748: if (scope != null) {
0749: params
0750: .remove(ScopedServletUtils.SCOPE_ID_PARAM);
0751: }
0752: }
0753:
0754: if (uri != null) {
0755: selectionLink = _res.encodeURL(uri);
0756: }
0757: }
0758: }
0759: return selectionLink;
0760: }
0761:
0762: /**
0763: * Render the icon for this node.
0764: * @param writer the appender where the tree markup is appended
0765: * @param node the node to render
0766: * @param attrs renderer for supported attributes
0767: * @param state the set of tree properties that are used to render the tree markup
0768: */
0769: protected void renderItemIcon(AbstractRenderAppender writer,
0770: TreeElement node, AttributeRenderer attrs,
0771: InheritableState state) {
0772: renderItemIconPrefix(writer, node);
0773:
0774: // There should always be one unless the tree turns off default
0775: // icons by setting the useDefaultIcons attribute to false.
0776: String icon = node.getIcon();
0777: if (icon == null) {
0778: icon = state.getIconRoot() + "/" + state.getItemIcon();
0779: } else {
0780: icon = state.getIconRoot() + "/" + icon;
0781: }
0782:
0783: // write out the icon
0784: if (icon != null) {
0785: _imgState.clear();
0786: _imgState.src = icon;
0787: _imgState.style = "vertical-align:text-top";
0788: String alt = null;
0789: String label = node.getLabel();
0790: if (label != null && node.isLabelLegalAsAlt())
0791: alt = label;
0792: else
0793: alt = node.getTitle();
0794: if (alt == null)
0795: alt = Bundle.getString("Tags_TreeAltText", null);
0796: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0797: ALT, alt, false);
0798: _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0799: BORDER, "0");
0800:
0801: // set the inheritted attributes
0802: attrs.renderIconImage(_imgState, node);
0803: _imageRenderer.doStartTag(writer, _imgState);
0804: _imageRenderer.doEndTag(writer);
0805: renderItemIconSuffix(writer, node);
0806: }
0807: }
0808:
0809: /**
0810: * Render the label for this node (if any).
0811: * @param writer the appender where the tree markup is appended
0812: * @param node the node to render
0813: */
0814: protected void renderLabel(AbstractRenderAppender writer,
0815: TreeElement node) {
0816: String label = node.getLabel();
0817: if (label != null) {
0818: renderLabelPrefix(writer, node);
0819: if (_trs.escapeContent) {
0820: HtmlUtils.filter(label, writer);
0821: } else {
0822: writer.append(label);
0823: }
0824: renderLabelSuffix(writer, node);
0825: }
0826: }
0827:
0828: /**
0829: * Render the Content for this node (if any).
0830: * @param writer the appender where the tree markup is appended
0831: * @param node the node to render
0832: */
0833: protected void renderContent(AbstractRenderAppender writer,
0834: TreeElement node) {
0835: String ctnt = node.getContent();
0836: if (ctnt != null) {
0837: renderContentPrefix(writer, node);
0838: if (_trs.escapeContent) {
0839: HtmlUtils.filter(ctnt, writer);
0840: } else {
0841: writer.append(ctnt);
0842: }
0843: renderContentSuffix(writer, node);
0844: }
0845: }
0846:
0847: // Methods that control formatting (beautifying) the node markup. Override these to
0848: // manage the white space displayed in the page or to add additional markup.
0849:
0850: /**
0851: * Render the formatting before a spacer image.
0852: * @param writer the appender where the tree markup is appended
0853: * @param node the node to render
0854: */
0855: protected void renderSpacerPrefix(AbstractRenderAppender writer,
0856: TreeElement node) {
0857: writer.append(FORMAT_INDENT);
0858: }
0859:
0860: /**
0861: * Render the formatting after a spacer image.
0862: * @param writer the appender where the tree markup is appended
0863: * @param node the node to render
0864: */
0865: protected void renderSpacerSuffix(AbstractRenderAppender writer,
0866: TreeElement node) {
0867: writer.append(FORMAT_NEWLINE);
0868: }
0869:
0870: /**
0871: * Render the formatting before a vertical line image.
0872: * @param writer the appender where the tree markup is appended
0873: * @param node the node to render
0874: */
0875: protected void renderVerticalLinePrefix(
0876: AbstractRenderAppender writer, TreeElement node) {
0877: writer.append(FORMAT_INDENT);
0878: }
0879:
0880: /**
0881: * Render the formatting following a vertical line image.
0882: * @param writer the appender where the tree markup is appended
0883: * @param node the node to render
0884: */
0885: protected void renderVerticalLineSuffix(
0886: AbstractRenderAppender writer, TreeElement node) {
0887: writer.append(FORMAT_NEWLINE);
0888: }
0889:
0890: /**
0891: * Render the formatting before the connecting/expand/collapse image.
0892: * @param writer the appender where the tree markup is appended
0893: * @param node the node to render
0894: */
0895: protected void renderConnectionImagePrefix(
0896: AbstractRenderAppender writer, TreeElement node) {
0897: writer.append(FORMAT_INDENT);
0898: }
0899:
0900: /**
0901: * Render the formatting after the connecting/expand/collapse image.
0902: * @param writer the appender where the tree markup is appended
0903: * @param node the node to render
0904: */
0905: protected void renderConnectionImageSuffix(
0906: AbstractRenderAppender writer, TreeElement node) {
0907: writer.append(FORMAT_NEWLINE);
0908: }
0909:
0910: /**
0911: * Render the formatting before the node selection anchor.
0912: * @param writer the appender where the tree markup is appended
0913: * @param node the node to render
0914: */
0915: protected void renderSelectionLinkPrefix(
0916: AbstractRenderAppender writer, TreeElement node) {
0917: writer.append(FORMAT_INDENT);
0918: }
0919:
0920: /**
0921: * Render the formatting after the node selection anchor.
0922: * @param writer the appender where the tree markup is appended
0923: * @param node the node to render
0924: */
0925: protected void renderSelectionLinkSuffix(
0926: AbstractRenderAppender writer, TreeElement node) {
0927: }
0928:
0929: /**
0930: * Render the formatting before the node icon.
0931: * @param writer the appender where the tree markup is appended
0932: * @param node the node to render
0933: */
0934: protected void renderItemIconPrefix(AbstractRenderAppender writer,
0935: TreeElement node) {
0936: writer.append(FORMAT_NBSP);
0937: }
0938:
0939: /**
0940: * Render the formatting after the node icon.
0941: * @param writer the appender where the tree markup is appended
0942: * @param node the node to render
0943: */
0944: protected void renderItemIconSuffix(AbstractRenderAppender writer,
0945: TreeElement node) {
0946: writer.append(FORMAT_NBSP);
0947: }
0948:
0949: /**
0950: * Render the formatting before the node label.
0951: * @param writer the appender where the tree markup is appended
0952: * @param node the node to render
0953: */
0954: protected void renderLabelPrefix(AbstractRenderAppender writer,
0955: TreeElement node) {
0956: }
0957:
0958: /**
0959: * Render the formatting after the node label.
0960: * @param writer the appender where the tree markup is appended
0961: * @param node the node to render
0962: */
0963: protected void renderLabelSuffix(AbstractRenderAppender writer,
0964: TreeElement node) {
0965: writer.append(FORMAT_NBSP);
0966: }
0967:
0968: /**
0969: * Render the formatting before the node content.
0970: * @param writer the appender where the tree markup is appended
0971: * @param node the node to render
0972: */
0973: protected void renderContentPrefix(AbstractRenderAppender writer,
0974: TreeElement node) {
0975: writer.append(FORMAT_NEWLINE_INDENT);
0976: }
0977:
0978: /**
0979: * Render the formatting after the node content.
0980: * @param writer the appender where the tree markup is appended
0981: * @param node the node to render
0982: */
0983: protected void renderContentSuffix(AbstractRenderAppender writer,
0984: TreeElement node) {
0985: }
0986:
0987: /**
0988: * Render the indent formatting of the start div tag used before the node markup.
0989: * @param writer the appender where the tree markup is appended
0990: * @param node the node to render
0991: */
0992: protected void renderStartDivPrefix(AbstractRenderAppender writer,
0993: TreeElement node) {
0994: writer.append(FORMAT_SHORT_INDENT);
0995: }
0996:
0997: /**
0998: * Render the formatting after the start div tag and before the node markup.
0999: * @param writer the appender where the tree markup is appended
1000: * @param node the node to render
1001: */
1002: protected void renderStartDivSuffix(AbstractRenderAppender writer,
1003: TreeElement node) {
1004: writer.append(FORMAT_NEWLINE);
1005: }
1006:
1007: /**
1008: * Render the indent formatting of the end div tag used after the node markup.
1009: * @param writer the appender where the tree markup is appended
1010: * @param node the node to render
1011: */
1012: protected void renderEndDivPrefix(AbstractRenderAppender writer,
1013: TreeElement node) {
1014: writer.append(FORMAT_NEWLINE_SHORT_INDENT);
1015: }
1016:
1017: /**
1018: * Render the formatting after the end div tag that follows the node markup.
1019: * @param writer the appender where the tree markup is appended
1020: * @param node the node to render
1021: */
1022: protected void renderEndDivSuffix(AbstractRenderAppender writer,
1023: TreeElement node) {
1024: writer.append(FORMAT_NEWLINE);
1025: }
1026: }
|