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.pageflow.requeststate.INameable;
0024: import org.apache.beehive.netui.pageflow.requeststate.NameService;
0025: import org.apache.beehive.netui.script.ExpressionUpdateException;
0026: import org.apache.beehive.netui.script.IllegalExpressionException;
0027: import org.apache.beehive.netui.tags.*;
0028: import org.apache.beehive.netui.tags.html.HtmlConstants;
0029: import org.apache.beehive.netui.tags.javascript.CoreScriptFeature;
0030: import org.apache.beehive.netui.tags.javascript.IScriptReporter;
0031: import org.apache.beehive.netui.tags.javascript.ScriptRequestState;
0032: import org.apache.beehive.netui.tags.rendering.AbstractHtmlState;
0033: import org.apache.beehive.netui.tags.rendering.DivTag;
0034: import org.apache.beehive.netui.tags.rendering.StringBuilderRenderAppender;
0035: import org.apache.beehive.netui.tags.rendering.TagRenderingBase;
0036: import org.apache.beehive.netui.util.Bundle;
0037: import org.apache.beehive.netui.core.urls.AjaxUrlInfo;
0038: import org.apache.beehive.netui.core.urls.URLRewriterService;
0039:
0040: import javax.servlet.http.HttpServletRequest;
0041: import javax.servlet.http.HttpServletResponse;
0042: import javax.servlet.jsp.JspException;
0043: import javax.servlet.jsp.PageContext;
0044: import java.io.IOException;
0045:
0046: /**
0047: * Netui tag that renders a tree control represented by a set of
0048: * <code>TreeElement</code> objects.
0049: * @jsptagref.tagdescription Renders a navigable tree of {@link TreeElement}
0050: * objects.
0051: *
0052: * <p>The tree is bound to a variable through the <code>dataSource</code> attribute.
0053: * If the bound variable has been initialized, then the body content of this tag is not
0054: * processed and the dataSource's version of the tree is rendered. If the bound variable
0055: * has not been initialized, we will process the body, create the <code>TreeElement</code>
0056: * tree structure, and set the bound variable with the newly created tree.
0057: *
0058: * A <netui:tree> tag cannot be nested within another <netui:tree> element.
0059: *
0060: * <p>This tag can automatically handle display icons for the tree nodes
0061: * through the <code>imageRoot</code> attribute. If you point the
0062: * <code>imageRoot</code> attribute at a folder containing appropriately
0063: * named image files, the correct images will be used for any given state
0064: * of the tree's nodes. The default image names are:
0065: *
0066: * <blockquote>
0067: * <ul>
0068: * <li>folder.gif</li>
0069: * <li>lastLineJoin.gif</li>
0070: * <li>lastNodeCollapsed.gif</li>
0071: * <li>lastNodeExpanded.gif</li>
0072: * <li>lineJoin.gif</li>
0073: * <li>nodeCollapsed.gif</li>
0074: * <li>nodeExpanded.gif</li>
0075: * <li>rootCollapsed.gif</li>
0076: * <li>rootExpanded.gif</li>
0077: * <li>spacer.gif</li>
0078: * <li>verticalLine.gif</li>
0079: * </ul>
0080: * </blockquote>
0081: *
0082: * @example The following example shows a <netui:tree> tag with a set of
0083: * children <netui:treeLabel> and <netui:treeItem> tags that form the
0084: * tree's navigational structure. The <code>dataSource</code> attribute
0085: * identifies the {@link TreeElement} in the appropriate binding context; in this case,
0086: * the page flow context is used. The <code>selectionAction</code> attribute
0087: * is required so there can be a postback to the tree. <code>tagId</code> is
0088: * used to uniquely identify a tree on the page.
0089: *
0090: * <pre> <netui:tree
0091: * dataSource="pageFlow.myTree"
0092: * selectionAction="postback"
0093: * tagId="myTree">
0094: * <netui:treeItem expanded="true" >
0095: * <netui:treeLabel>Root Folder</netui:treeLabel>
0096: * <netui:treeItem expanded="false">
0097: * <netui:treeLabel>I</netui:treeLabel>
0098: * <netui:treeItem expanded="false">
0099: * <netui:treeLabel>A</netui:treeLabel>
0100: * <netui:treeItem>1</netui:treeItem>
0101: * <netui:treeItem>2</netui:treeItem>
0102: * </netui:treeItem>
0103: * </netui:treeItem>
0104: * </netui:treeItem>
0105: * </netui:tree></pre>
0106: * @netui:tag name="tree" body-content="scriptless" description="Renders a tree control represented by a set of TreeElement objects."
0107: */
0108: public class Tree extends AbstractSimpleTag implements HtmlConstants,
0109: IAttributeConsumer, IErrorCollector {
0110: /**
0111: * The name of the directory containing the images for our icons,
0112: * relative to the page including this tag.
0113: */
0114: private TreeElement _rootNode = null;
0115:
0116: private TreeRenderState _trs;
0117:
0118: // The new style and class attributes
0119: private String _treeStyle; // tree style
0120: private String _treeStyleClass; // tree class
0121:
0122: private InternalStringBuilder _errorText; // Text of any error that may have occurred.
0123:
0124: private String _dataSource = null; // The name of the tree.
0125: private boolean _renderTagIdLookup = false; // cause the base javascript support to be output
0126:
0127: private InheritableState _iState = new InheritableState();
0128: private String _rootNodeCollapsedImage;
0129: private String _rootNodeExpandedImage;
0130:
0131: // These are used in a stateless manner. They do not have to be cleared
0132: // in the local release because each state is cleared before it is set to write a start tag.
0133: private DivTag.State _divState = new DivTag.State();
0134:
0135: private ExpressionHandling _expr;
0136:
0137: public Tree() {
0138: _trs = new TreeRenderState();
0139: _iState.initalizeTreeState();
0140: }
0141:
0142: /**
0143: * Return the name of the Tag.
0144: */
0145: public String getTagName() {
0146: return "Tree";
0147: }
0148:
0149: /**
0150: * Sets the action used for expanding and contracting tree nodes. This action will be
0151: * inherited by the <code>TreeElements</code> inside the tree.
0152: * @param action the action
0153: * @jsptagref.attributedescription Sets the action used for expanding and contracting tree nodes.
0154: * @jsptagref.databindable false
0155: * @jsptagref.attributesyntaxvalue <i>string_action</i>
0156: * @netui:attribute required="false" rtexprvalue="true"
0157: * description="Sets the action used for expanding and contracting tree nodes."
0158: */
0159: public void setExpansionAction(String action) throws JspException {
0160: _iState.setExpansionAction(setRequiredValueAttribute(action,
0161: "expansionAction"));
0162: }
0163:
0164: /**
0165: * Sets the action used for selecting tree nodes. This action will be
0166: * inherited by the <code>TreeElements</code> inside the tree.
0167: * @param action the action
0168: * @jsptagref.attributedescription Sets the action used when a tree node is selected.
0169: * @jsptagref.databindable false
0170: * @jsptagref.attributesyntaxvalue <i>string_action</i>
0171: * @netui:attribute required="true" rtexprvalue="true"
0172: * description="Sets the action used when a tree node is selected."
0173: */
0174: public void setSelectionAction(String action) throws JspException {
0175: _iState.setSelectionAction(setRequiredValueAttribute(action,
0176: "selectionAction"));
0177: }
0178:
0179: /**
0180: * Return the action for selection action on the tree. This action will be called
0181: * when a TreeElement is selected.
0182: * @return the selection action name.
0183: */
0184: public String getSelectionAction() {
0185: return _iState.getSelectionAction();
0186: }
0187:
0188: /**
0189: * Sets the target for a selection action. This can specify the name
0190: * of the frame where the document is to be opened.
0191: * @param target the target for selection
0192: * @jsptagref.attributedescription Sets the frame target used for selecting tree nodes.
0193: * @jsptagref.databindable false
0194: * @jsptagref.attributesyntaxvalue <i>string_target</i>
0195: * @netui:attribute rtexprvalue="true"
0196: * description="Sets the frame target used for selecting tree nodes."
0197: */
0198: public void setSelectionTarget(String target) {
0199: _iState.setSelectionTarget(setNonEmptyValueAttribute(target));
0200: }
0201:
0202: /**
0203: * Set the ID of the tag. This is required and will not generate the
0204: * standard JavaScript lookup code unless <code>RenderTagIdLookup</code>
0205: * is set to <code>true</code>.
0206: * @param tagId the tagId.
0207: * @jsptagref.attributedescription Set the id of the tree. This appears on the containing <div>.
0208: * @jsptagref.databindable false
0209: * @jsptagref.attributesyntaxvalue <i>string_tagId</i>
0210: * @netui:attribute required="true" rtexprvalue="true"
0211: * description="Set the id of the tree. This appears on the containing <div>."
0212: */
0213: public void setTagId(String tagId) throws JspException {
0214: _trs.tagId = setRequiredValueAttribute(tagId, "tagId");
0215: }
0216:
0217: /**
0218: * Sets the image name for an open non-leaf node with no
0219: * line below it. (Defaults to "lastNodeExpanded.gif").
0220: * @param lastNodeExpandedImage the image name (including extension)
0221: * @jsptagref.attributedescription The image name for an open non-leaf node with no line below it. (Defaults to "lastNodeExpanded.gif".)
0222: * @jsptagref.databindable false
0223: * @jsptagref.attributesyntaxvalue <i>string_imageHandleDownLast</i>
0224: * @netui:attribute required="false" rtexprvalue="true"
0225: * description="Sets the image name for an open non-leaf node with no
0226: * line below it."
0227: */
0228: public void setLastNodeExpandedImage(String lastNodeExpandedImage) {
0229: String val = setNonEmptyValueAttribute(lastNodeExpandedImage);
0230: if (val != null)
0231: _iState
0232: .setLastNodeExpandedImage(setNonEmptyValueAttribute(val));
0233: }
0234:
0235: /**
0236: * Sets the image name for an open non-leaf node with a
0237: * line below it. (Defaults to "nodeExpanded.gif").
0238: * @param nodeExpandedImage the image name (including extension)
0239: * @jsptagref.attributedescription The image name for an open non-leaf node with a
0240: * line below it. (Defaults to "nodeExpanded.gif".)
0241: * @jsptagref.databindable false
0242: * @jsptagref.attributesyntaxvalue <i>string_imageHandleDownMiddle</i>
0243: * @netui:attribute required="false" rtexprvalue="true"
0244: * description="Sets the image name for an open non-leaf node with a
0245: * line below it."
0246: */
0247: public void setNodeExpandedImage(String nodeExpandedImage) {
0248: String val = setNonEmptyValueAttribute(nodeExpandedImage);
0249: if (val != null)
0250: _iState
0251: .setNodeExpandedImage(setNonEmptyValueAttribute(val));
0252: }
0253:
0254: /**
0255: * Sets the image name for a closed non-leaf node with no
0256: * line below it. (Defaults to "lastNodeCollapsed.gif").
0257: * @param lastNodeCollapsedImage the image name (including extension)
0258: * @jsptagref.attributedescription The image name for a closed non-leaf node with no
0259: * line below it. (Defaults to "lastNodeCollapsed.gif".)
0260: * @jsptagref.databindable false
0261: * @jsptagref.attributesyntaxvalue <i>string_imageHandleRightLast</i>
0262: * @netui:attribute required="false" rtexprvalue="true"
0263: * description="Sets the image name for a closed non-leaf node with no
0264: * line below it."
0265: */
0266: public void setLastNodeCollapsedImage(String lastNodeCollapsedImage) {
0267: String val = setNonEmptyValueAttribute(lastNodeCollapsedImage);
0268: if (val != null)
0269: _iState
0270: .setLastNodeCollapsedImage(setNonEmptyValueAttribute(val));
0271: }
0272:
0273: /**
0274: * Sets the image name for a closed non-leaf node with a
0275: * line below it. (Defaults to "nodeCollapsed.gif").
0276: * @param nodeCollapsedImage the image name (including extension)
0277: * @jsptagref.attributedescription The image name for a closed non-leaf node with a
0278: * line below it. (Defaults to "nodeCollapsed.gif".)
0279: * @jsptagref.databindable false
0280: * @jsptagref.attributesyntaxvalue <i>string_imageHandleRightMiddle</i>
0281: * @netui:attribute required="false" rtexprvalue="true"
0282: * description="Sets the image name for a closed non-leaf node with a
0283: * line below it."
0284: */
0285: public void setNodeCollapsedImage(String nodeCollapsedImage) {
0286: String val = setNonEmptyValueAttribute(nodeCollapsedImage);
0287: if (val != null)
0288: _iState
0289: .setNodeCollapsedImage(setNonEmptyValueAttribute(val));
0290: }
0291:
0292: /**
0293: * Sets the image name for a blank area of the tree.
0294: * (Defaults to "lastLineJoin.gif").
0295: * @param lastLineJoinImage the image name (including extension)
0296: * @jsptagref.attributedescription The image name for a blank area of the tree.
0297: * (Defaults to "lastLineJoin.gif")
0298: * @jsptagref.databindable false
0299: * @jsptagref.attributesyntaxvalue <i>string_imageLineLast</i>
0300: * @netui:attribute required="false" rtexprvalue="true"
0301: * description="Sets the image name for a blank area of the tree."
0302: */
0303: public void setLastLineJoinImage(String lastLineJoinImage) {
0304: String val = setNonEmptyValueAttribute(lastLineJoinImage);
0305: if (val != null)
0306: _iState
0307: .setLastLineJoinImage(setNonEmptyValueAttribute(val));
0308: }
0309:
0310: /**
0311: * Sets the image name for a spacer image used to align the other images inside the tree. (Defaults to "spacer.gif").
0312: * @param spacerImage the image name (including extension)
0313: * @jsptagref.attributedescription Sets the image name for a spacer image used to align the other images inside the tree.
0314: * (Defaults to "spacer.gif").
0315: * @jsptagref.databindable false
0316: * @jsptagref.attributesyntaxvalue <i>string_spacerImage</i>
0317: * @netui:attribute required="false" rtexprvalue="true"
0318: * description="Sets the image name for a spacer image used to align the other images inside the tree."
0319: */
0320: public void setSpacerImage(String spacerImage) {
0321: String val = setNonEmptyValueAttribute(spacerImage);
0322: if (val != null)
0323: _iState.setImageSpacer(setNonEmptyValueAttribute(val));
0324: }
0325:
0326: /**
0327: * Sets the default icon for TreeElements for a blank area of the tree.
0328: * (Defaults to "folder.gif").
0329: * @param itemIcon the image name of the itemIcon
0330: * @jsptagref.attributedescription Sets the default icon for TreeElements for a blank area of the tree.
0331: * (Defaults to "folder.gif").
0332: * @jsptagref.databindable false
0333: * @jsptagref.attributesyntaxvalue <i>string_itemIcon</i>
0334: * @netui:attribute required="false" rtexprvalue="true"
0335: * description="Sets the default icon for TreeElements for a blank area of the tree."
0336: */
0337: public void setItemIcon(String itemIcon) {
0338: String val = setNonEmptyValueAttribute(itemIcon);
0339: if (val != null)
0340: _iState.setItemIcon(setNonEmptyValueAttribute(val));
0341: }
0342:
0343: /**
0344: * Sets the image name for an area with a line through it.
0345: * (Defaults to "lineJoin.gif").
0346: * @param lineJoinImage the image name (including extension)
0347: * @jsptagref.attributedescription The image name for an area with a line through it.
0348: * (Defaults to "lineJoin.gif").
0349: * @jsptagref.databindable false
0350: * @jsptagref.attributesyntaxvalue <i>string_imageLineMiddle</i>
0351: * @netui:attribute required="false" rtexprvalue="true"
0352: * description="Sets the image name for an area with a line through it."
0353: */
0354: public void setLineJoinImage(String lineJoinImage) {
0355: String val = setNonEmptyValueAttribute(lineJoinImage);
0356: if (val != null)
0357: _iState.setLineJoinImage(setNonEmptyValueAttribute(val));
0358: }
0359:
0360: /**
0361: * Sets the image name for an area with a line through it.
0362: * (Defaults to "verticalLine.gif").
0363: * @param verticalLineImage the image name (including extension)
0364: * @jsptagref.attributedescription The image name for an area with a line through it.
0365: * (Defaults to "verticalLine.gif").
0366: * @jsptagref.databindable false
0367: * @jsptagref.attributesyntaxvalue <i>string_imageLIneVertical</i>
0368: * @netui:attribute required="false" rtexprvalue="true"
0369: * description="Sets the image name for an area with a line through it."
0370: */
0371: public void setVerticalLineImage(String verticalLineImage) {
0372: String val = setNonEmptyValueAttribute(verticalLineImage);
0373: if (val != null)
0374: _iState
0375: .setVerticalLineImage(setNonEmptyValueAttribute(val));
0376: }
0377:
0378: /**
0379: * Sets the name of the directory containing the images. This should be specified
0380: * absolutely within the webapp. This target will provide images for the structure of
0381: * the tree. If the <code>iconRoot</code> is not set, then this will also specify where
0382: * the icons come are found.
0383: * @param imageRoot the directory name
0384: * @jsptagref.attributedescription The directory containing the images for tree icons.
0385: * @jsptagref.databindable false
0386: * @jsptagref.attributesyntaxvalue <i>string_imageRoot</i>
0387: * @netui:attribute required="false" rtexprvalue="true"
0388: * description="Sets the name of the directory containing the images for our icons,
0389: * and structure of the tree."
0390: */
0391: public void setImageRoot(String imageRoot) {
0392: _iState.setImageRoot(setNonEmptyValueAttribute(imageRoot));
0393: }
0394:
0395: /**
0396: * This will set the location of the icon images. When the location
0397: * is explicitly set, this works exactly the same as all other inheritable
0398: * properties. When this is not set, it will return the <code>getImageRoot</code>
0399: * location.
0400: * @param iconRoot the directory name for the icons
0401: * @jsptagref.attributedescription The directory containing the icon images for tree items.
0402: * @jsptagref.databindable false
0403: * @jsptagref.attributesyntaxvalue <i>string_iconRoot</i>
0404: * @netui:attribute required="false" rtexprvalue="true"
0405: * description="Sets the name of the directory containing the images for our icons."
0406: */
0407: public void setIconRoot(String iconRoot) {
0408: _iState.setIconRoot(iconRoot);
0409: }
0410:
0411: /**
0412: * Sets the image that will be used for the root when it is Collapsed. The root must implement
0413: * ITreeRootElement to support this. (Defaults to "rootCollapsed.gif").
0414: * @param rootNodeCollapsedImage the image representing a root that is collapsed.
0415: * @jsptagref.attributedescription The image representing the root when it is collapsed.
0416: * (Defaults to "rootCollapsed.gif").
0417: * @jsptagref.databindable true
0418: * @jsptagref.attributesyntaxvalue <i>string_rootNodeCollapsedImage</i>
0419: * @netui:attribute required="false" rtexprvalue="true"
0420: * description="Sets the image representing the root when it is collapsed."
0421: */
0422: public void setRootNodeCollapsedImage(String rootNodeCollapsedImage) {
0423: _rootNodeCollapsedImage = setNonEmptyValueAttribute(rootNodeCollapsedImage);
0424: }
0425:
0426: /**
0427: * Sets the image that will be used for the root when it is Expanded. The root must implement
0428: * ITreeRootElement to support this. (Defaults to "rootExpanded.gif").
0429: * @param rootNodeExpandedImage the image representing a root that is expanded.
0430: * @jsptagref.attributedescription The image representing the root when it is expanded.
0431: * (Defaults to "rootExpanded.gif").
0432: * @jsptagref.databindable true
0433: * @jsptagref.attributesyntaxvalue <i>string_rootNodeExpandedImage</i>
0434: * @netui:attribute required="false" rtexprvalue="true"
0435: * description="Sets the image representing the root when it is expanded."
0436: */
0437: public void setRootNodeExpandedImage(String rootNodeExpandedImage) {
0438: _rootNodeExpandedImage = setNonEmptyValueAttribute(rootNodeExpandedImage);
0439: }
0440:
0441: /**
0442: * Cause expansion and contraction on the client. When this is set, the
0443: * expand and collapse events will be handled on the client. TreeElements which are hidden
0444: * becauses other TreeElements are collapsed will be rendered to the page and then displayed
0445: * by JavaScript on the page. These expansion and contraction events will not call
0446: * the server's expansion action.
0447: * @param runAtClient
0448: * @jsptagref.attributedescription Indicates whether that base expand and contraction will happen
0449: * on the client.
0450: * @jsptagref.databindable false
0451: * @jsptagref.attributesyntaxvalue <i>boolean_runAtClient</i>
0452: * @netui:attribute required="false" rtexprvalue="false" type="boolean"
0453: * description="Indicates whether that base expand and contraction will happen on the client."
0454: */
0455: public void setRunAtClient(boolean runAtClient) {
0456: _trs.runAtClient = runAtClient;
0457: }
0458:
0459: /**
0460: * This attribue will cause the content of labels to be escaped when the value if <i>true</i>.
0461: * The default value is <i>false</i>.
0462: * @param htmlEscape
0463: * @jsptagref.attributedescription When true the content of labels will be escaped for HTML.
0464: * @jsptagref.databindable false
0465: * @jsptagref.attributesyntaxvalue <i>boolean_htmlEscape</i>
0466: * @netui:attribute required="false" type="boolean"
0467: * description="When true the content of labels will be escaped for HTML."
0468: */
0469: public void setEscapeForHtml(boolean htmlEscape) {
0470: _trs.escapeContent = htmlEscape;
0471: }
0472:
0473: /**
0474: * This will cause the standard tagId to Id JavaScript to be output. For most
0475: * of the HTML tags this is automatically output. For the tree, because the tagId
0476: * is a required attribute, we make javascript support optional.
0477: * @param renderTagIdLookup
0478: * @jsptagref.attributedescription Output the standard tagId to Id JavaScript.
0479: * @jsptagref.databindable false
0480: * @jsptagref.attributesyntaxvalue <i>boolean_renderTagIdLookup</i>
0481: * @netui:attribute required="false" type="boolean"
0482: * description="Output the standard tagId to Id JavaScript."
0483: */
0484: public void setRenderTagIdLookup(boolean renderTagIdLookup) {
0485: _renderTagIdLookup = renderTagIdLookup;
0486: }
0487:
0488: /**
0489: * Sets the root <code>TreeElement</code> of this tree.
0490: * @param rootNode the root treeNode
0491: */
0492: public void setRootNode(TreeElement rootNode) {
0493: _rootNode = rootNode;
0494: if (rootNode.getName() == null) {
0495: rootNode.setName("0");
0496: }
0497: }
0498:
0499: /**
0500: * Return the root node of the tree.
0501: * @return returns the root node of the tree.
0502: */
0503: public TreeElement getRootNode() {
0504: return _rootNode;
0505: }
0506:
0507: /**
0508: * Set the style of a tree element when is is selected. This results in a <code>style</code>
0509: * attribute being generated for this tree node.
0510: * @param selectedStyle
0511: * @jsptagref.attributedescription Set the style of a tree element when is is selected. This results in a
0512: * <code>style</code> attribute being generated for this tree node.
0513: * @jsptagref.databindable false
0514: * @jsptagref.attributesyntaxvalue <i>string_selectedStyle</i>
0515: * @netui:attribute required="false" rtexprvalue="true"
0516: * description="Set the style of a tree element when is is selected. This results in a <code>style</code>
0517: * attribute being generated for this tree node."
0518: */
0519: public void setSelectedStyle(String selectedStyle) {
0520: _trs.selectedStyle = setNonEmptyValueAttribute(selectedStyle);
0521: }
0522:
0523: /**
0524: * Set the style class of a tree element when is is disabled. A disabled element will have
0525: * a style that can be expanded/contracted, but may not be selected.
0526: * @param disableStyleClass
0527: * @jsptagref.attributedescription Set the style class of a tree element when is is disabled. A
0528: * disabled element will have a style that may be expanded/contracted, but may not be selected.
0529: * @jsptagref.databindable false
0530: * @jsptagref.attributesyntaxvalue <i>string_disableStyleClass</i>
0531: * @netui:attribute required="false" rtexprvalue="true"
0532: * description="Set the style class of a tree element when is is disabled. A disabled element will have
0533: * a style that can be expanded/contracted, but may not be selected."
0534: */
0535:
0536: public void setDisabledStyleClass(String disableStyleClass) {
0537: _trs.disabledStyleClass = setNonEmptyValueAttribute(disableStyleClass);
0538: }
0539:
0540: /**
0541: * Set the style of a tree element when is is disabled. A disabled element will have
0542: * a style that can be expanded/contracted, but may not be selected.
0543: * @param disabledStyle
0544: * @jsptagref.attributedescription Set the style of a tree element when is is disabled. A
0545: * disabled element will have a style that can be expanded/contracted, but may not be selected.
0546: * @jsptagref.databindable false
0547: * @jsptagref.attributesyntaxvalue <i>string_disableStyle</i>
0548: * @netui:attribute required="false" rtexprvalue="true"
0549: * description="Set the style of a tree element when is is disabled. A disabled element will have
0550: * a style that can be expanded/contracted, but may not be selected."
0551: */
0552: public void setDisabledStyle(String disabledStyle) {
0553: _trs.disabledStyle = setNonEmptyValueAttribute(disabledStyle);
0554: }
0555:
0556: /**
0557: * Set the style class of a tree element when is is selected. This results in a <code>class</code>
0558: * attribute being generated for this tree node.
0559: * @param selectedStyleClass
0560: * @jsptagref.attributedescription Set the style class of a tree element when is is selected. This
0561: * results in a <code>class</code> attribute being generated for this tree node.
0562: * @jsptagref.databindable false
0563: * @jsptagref.attributesyntaxvalue <i>string_selectedStyleClass</i>
0564: * @netui:attribute required="false" rtexprvalue="true"
0565: * description="Set the style class of a tree element when is is selected. This results in a <code>class</code>
0566: * attribute being generated for this tree node."
0567: */
0568: public void setSelectedStyleClass(String selectedStyleClass) {
0569: _trs.selectedStyleClass = setNonEmptyValueAttribute(selectedStyleClass);
0570: }
0571:
0572: /**
0573: * Set the style class of a tree element when is is not selected. This results in a <code>style</code>
0574: * attribute being generated for this tree node.
0575: * @param unselectedStyle
0576: * @jsptagref.attributedescription Set the style of a tree element when is is not selected. This
0577: * results in a <code>style</code> attribute being generated for this tree node.
0578: * @jsptagref.databindable false
0579: * @jsptagref.attributesyntaxvalue <i>string_unselectedStyle</i>
0580: * @netui:attribute required="false" rtexprvalue="true"
0581: * description="Set the style of a tree element when is is not selected. This results in a <code>style</code>
0582: * attribute being generated for this tree node."
0583: */
0584: public void setUnselectedStyle(String unselectedStyle) {
0585: _trs.unselectedStyle = setNonEmptyValueAttribute(unselectedStyle);
0586: }
0587:
0588: /**
0589: * Set the style class of a tree element when is is selected. This results in a <code>class</code>
0590: * attribute being generated for this tree node.
0591: * @param unselectedStyleClass
0592: * @jsptagref.attributedescription Set the style class of a tree element when is is not selected. This
0593: * results in a <code>style</code> attribute being generated for this tree node.
0594: * @jsptagref.databindable false
0595: * @jsptagref.attributesyntaxvalue <i>string_unselectedStyleClasss</i>
0596: * @netui:attribute required="false" rtexprvalue="true"
0597: * description="Set the style class of a tree element when is is selected. This results in a <code>class</code>
0598: * attribute being generated for this tree node."
0599: */
0600: public void setUnselectedStyleClass(String unselectedStyleClass) {
0601: _trs.unselectedStyleClass = setNonEmptyValueAttribute(unselectedStyleClass);
0602: }
0603:
0604: /**
0605: * Sets the <code>style</code> attribute of the tree.
0606: * @param treeStyle the style
0607: * @jsptagref.attributedescription Sets the <code>style</code> attribute of the tree.
0608: * @jsptagref.databindable false
0609: * @jsptagref.attributesyntaxvalue <i>string_treeStyle</i>
0610: * @netui:attribute required="false" rtexprvalue="true"
0611: * description="Sets the <code>style</code> attribute of the tree."
0612: */
0613: public void setTreeStyle(String treeStyle) {
0614: _treeStyle = setNonEmptyValueAttribute(treeStyle);
0615: }
0616:
0617: /**
0618: * Sets the <code>class</code> attribute of the tree. This will be set
0619: * on the containing <div> for the tree.
0620: * @param treeStyleClass the style
0621: * @jsptagref.attributedescription Sets the <code>class</code> attribute of the tree.
0622: * @jsptagref.databindable false
0623: * @jsptagref.attributesyntaxvalue <i>string_treeStyleClass</i>
0624: * @netui:attribute required="false" rtexprvalue="true"
0625: * description="Sets the <code>class</code> attribute of the tree."
0626: */
0627: public void setTreeStyleClass(String treeStyleClass) {
0628: _treeStyleClass = setNonEmptyValueAttribute(treeStyleClass);
0629: }
0630:
0631: /**
0632: * Sets an expression which indentifies the TreeElement that represents the root of
0633: * the tree. When the variable being bound to is not null, the body of the Tree
0634: * will be ignored and the tree being bound to will be rendered. If the variable
0635: * being bound to is null, then the body of the tree will be processed to create
0636: * the initial tree state and the bound variable will be set. In this situation
0637: * the property must support both read and write.
0638: * @param dataSource the tree attribute name
0639: * @jsptagref.attributedescription sets an expression which indentifies the TreeElement
0640: * that represents the root of the tree.
0641: * @jsptagref.databindable true
0642: * @jsptagref.attributesyntaxvalue <i>string_dataSource</i>
0643: * @netui:attribute required="true"
0644: * description="Sets an expression which indentifies the TreeElement that represents the root of
0645: * the tree."
0646: */
0647: public void setDataSource(String dataSource) {
0648: _dataSource = dataSource;
0649: }
0650:
0651: /**
0652: * Set an attribute value on the implementing class. The <code>name</code> represents
0653: * the name of the attribute. The <code>value</code> represents the value and may contains
0654: * an expression. The <code>facet</code> is optional and may be used by complex types to
0655: * target the attribute to a sub part of the generated markup. This method may result in errors
0656: * being generated.
0657: * @param name The name of the attribute. This value may not be null or the empty string.
0658: * @param value The value of the attribute. This may contain an expression.
0659: * @param facet The name of a facet to which the attribute will be applied. This is optional.
0660: * @throws javax.servlet.jsp.JspException A JspException may be thrown if there is an error setting the attribute.
0661: */
0662: public void setAttribute(String name, String value, String facet)
0663: throws JspException {
0664: // validate the name attribute, in the case of an error simply return.
0665: if (name == null || name.length() <= 0) {
0666: String s = Bundle.getString("Tags_AttributeNameNotSet");
0667: registerTagError(s, null);
0668: return;
0669: }
0670: // it's not legal to set the id or name attributes this way
0671: if (name != null && (name.equals("netui:treeName"))) {
0672: String s = Bundle.getString("Tags_AttributeMayNotBeSet",
0673: new Object[] { name });
0674: registerTagError(s, null);
0675: return;
0676: }
0677:
0678: _divState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0679: name, value);
0680: }
0681:
0682: /**
0683: * Prepare the Tree for rendering.
0684: * @throws JspException if a JSP exception has occurred
0685: */
0686: public void doTag() throws JspException, IOException {
0687: if (hasErrors()) {
0688: reportErrors();
0689: return;
0690: }
0691:
0692: // See if there is a TreeRoot already defined.
0693: _expr = new ExpressionHandling(this );
0694: TreeElement root = null;
0695: try {
0696: root = getTreeRoot(_expr);
0697: } catch (IllegalExpressionException iee) {
0698: String s = Bundle.getString("TreeRootError", new Object[] {
0699: _dataSource, iee.getMessage() });
0700: registerTagError(s, null);
0701: return;
0702: }
0703: if (hasErrors()) {
0704: reportErrors();
0705: return;
0706: }
0707:
0708: // If we don't have a root element, then we need to create it by processing the body which contains
0709: // /*<TreeItems>*/ tags that define the tree.
0710: if (root == null) {
0711: getBufferBody(false);
0712:
0713: // check to see if we should exit due to an error occuring
0714: // write out any error text and return.
0715: if (_errorText != null) {
0716: write(_errorText.toString());
0717: if (hasErrors())
0718: reportErrors();
0719: }
0720: }
0721:
0722: // Set the image Root if it is not set.
0723: PageContext pageContext = getPageContext();
0724: if (_iState.getImageRoot() == null)
0725: _iState.setImageRoot(((HttpServletRequest) pageContext
0726: .getRequest()).getContextPath()
0727: + "/" + TagConfig.getTreeImageLocation());
0728:
0729: // errors should have been caught above
0730: TreeElement treeRoot = getTreeRoot(_expr);
0731:
0732: // if the tree root hasn't been defined, then we need to update the object that is
0733: // pointed at by the dataSource expression.
0734: if (treeRoot == null) {
0735: if (_rootNode != null) {
0736: try {
0737: String datasource = "{" + _dataSource + "}";
0738: _expr.updateExpression(datasource, _rootNode,
0739: pageContext);
0740: } catch (ExpressionUpdateException e) {
0741: String s = Bundle.getString(
0742: "Tags_UnableToWriteTree", new Object[] {
0743: _dataSource, e.getMessage() });
0744: registerTagError(s, null);
0745: reportErrors();
0746: return;
0747: }
0748: treeRoot = _rootNode;
0749: }
0750:
0751: // indicate an update error and return
0752: if (treeRoot == null) {
0753: String s = Bundle.getString("Tags_TreeNoAttribute",
0754: _dataSource);
0755: registerTagError(s, null);
0756: reportErrors();
0757: return;
0758: }
0759:
0760: }
0761:
0762: // set the root image
0763: if (treeRoot instanceof ITreeRootElement) {
0764: ITreeRootElement tre = (ITreeRootElement) treeRoot;
0765: if (tre.getRootNodeExpandedImage() == null) {
0766: tre
0767: .setRootNodeExpandedImage((_rootNodeExpandedImage != null) ? _rootNodeExpandedImage
0768: : InheritableState.IMAGE_ROOT_EXPANDED);
0769: }
0770: if (tre.getRootNodeCollapsedImage() == null) {
0771: tre
0772: .setRootNodeCollapsedImage((_rootNodeCollapsedImage != null) ? _rootNodeCollapsedImage
0773: : InheritableState.IMAGE_ROOT_COLLAPSED);
0774: }
0775: }
0776:
0777: // if we are running the tree at the client, then
0778: // we need to register the tree with the NameService
0779: if (_trs.runAtClient) {
0780: // it's currently not legal to have a runAtClient but not be an instance of INameable which is
0781: // implemented by the ITreeRootElement.
0782: if (!(treeRoot instanceof INameable)) {
0783: String s = Bundle.getString("Tags_TreeRunAtClientRoot",
0784: null);
0785: registerTagError(s, null);
0786: reportErrors();
0787: return;
0788: }
0789:
0790: // name the tree if it hasn't been named already
0791: // or if no longer stored in the NameService, add it.
0792: INameable in = (INameable) treeRoot;
0793: String o = in.getObjectName();
0794: NameService ns = NameService.instance(pageContext
0795: .getSession());
0796: if (o == null) {
0797: ns.nameObject("Tree", in);
0798: ns.put(in);
0799: } else if (ns.get(o) == null) {
0800: ns.put(in);
0801: }
0802: }
0803:
0804: // prepare to render the tree
0805: HttpServletRequest request = (HttpServletRequest) pageContext
0806: .getRequest();
0807: HttpServletResponse response = (HttpServletResponse) pageContext
0808: .getResponse();
0809: InternalStringBuilder sb = new InternalStringBuilder(1024);
0810: StringBuilderRenderAppender writer = new StringBuilderRenderAppender(
0811: sb);
0812:
0813: // this is the treeId from the request. If there was an tree expansion this will be
0814: // non-null and it identifies what tree had the expansion request.
0815: // we need to qualify the tree based upon the tagId
0816: assert (_trs.tagId != null);
0817: _trs.tagId = getIdForTagId(_trs.tagId);
0818:
0819: String treeId = request.getParameter(TreeElement.TREE_ID);
0820: if (treeId != null && _trs.tagId.equals(treeId)) {
0821: TreeHelpers.processTreeRequest(treeId, treeRoot, request,
0822: response);
0823: }
0824:
0825: // check for the nodes that are expanded...
0826: // Add the script support for the tree.
0827: if (_trs.runAtClient) {
0828: IScriptReporter sr = getScriptReporter();
0829: if (sr == null) {
0830: String s = Bundle.getString("Tags_TreeRunAtClientSC",
0831: null);
0832: registerTagError(s, null);
0833: reportErrors();
0834: return;
0835: }
0836:
0837: ScriptRequestState srs = ScriptRequestState
0838: .getScriptRequestState(request);
0839: if (!srs.isFeatureWritten(CoreScriptFeature.DYNAMIC_INIT)) {
0840: String s = Bundle.getString("Tags_TreeHtmlRunAtClient",
0841: null);
0842: registerTagError(s, null);
0843: reportErrors();
0844: return;
0845: }
0846:
0847: assert (treeRoot instanceof ITreeRootElement);
0848: ITreeRootElement tre = (ITreeRootElement) treeRoot;
0849:
0850: Object[] args = new Object[8];
0851: args[0] = _iState.getImageRoot() + "/";
0852: args[1] = tre.getObjectName();
0853: args[2] = _iState.getNodeCollapsedImage();
0854: args[3] = _iState.getNodeExpandedImage();
0855: args[4] = _iState.getLastNodeCollapsedImage();
0856: args[5] = _iState.getLastNodeExpandedImage();
0857: args[6] = Bundle.getString("Tags_TreeAltTextExpand", null);
0858: args[7] = Bundle
0859: .getString("Tags_TreeAltTextCollapse", null);
0860: srs.writeFeature(sr, writer, CoreScriptFeature.TREE_INIT,
0861: false, false, args);
0862:
0863: AjaxUrlInfo ajaxInfo = URLRewriterService.getAjaxUrl(
0864: pageContext.getServletContext(), request, treeRoot);
0865: if (ajaxInfo.getCommandPrefix() != null) {
0866: args = new Object[2];
0867: args[0] = tre.getObjectName();
0868: args[1] = ajaxInfo.getCommandPrefix();
0869: srs.writeFeature(sr, writer,
0870: CoreScriptFeature.AJAX_PREFIX, false, false,
0871: args);
0872: }
0873: if (ajaxInfo.getAjaxParameter() != null) {
0874: args = new Object[2];
0875: args[0] = tre.getObjectName();
0876: args[1] = ajaxInfo.getAjaxParameter();
0877: srs.writeFeature(sr, writer,
0878: CoreScriptFeature.AJAX_PARAM, false, false,
0879: args);
0880: }
0881:
0882: tre.setTreeRenderState(_trs);
0883: tre.setInheritableState(_iState);
0884: }
0885:
0886: // create a containing tree level <div> and place the tree level styles on it.
0887: _divState.styleClass = _treeStyleClass;
0888: _divState.style = _treeStyle;
0889: String divId = null;
0890: if (_renderTagIdLookup) {
0891: _divState.id = _trs.tagId;
0892: divId = _divState.id;
0893: }
0894:
0895: // if we are running on the client then we need to output the tree name into the top level tree <div> tag
0896: if (_trs.runAtClient) {
0897: _divState.registerAttribute(AbstractHtmlState.ATTR_GENERAL,
0898: "netui:treeName", ((INameable) treeRoot)
0899: .getObjectName());
0900: }
0901:
0902: TagRenderingBase divRenderer = TagRenderingBase.Factory
0903: .getRendering(TagRenderingBase.DIV_TAG, request);
0904: divRenderer.doStartTag(writer, _divState);
0905: sb.append("\n");
0906:
0907: // Render the tree.
0908: AttributeRenderer extraAttrs = new AttributeRenderer();
0909: String treeRendererClassName = TagConfig
0910: .getTreeRendererClassName();
0911: TreeRenderer tr = TreeRendererFactory
0912: .getInstance(treeRendererClassName);
0913: if (tr == null) {
0914: tr = new TreeRenderer();
0915: }
0916: tr.init(_trs, request, response, pageContext
0917: .getServletContext());
0918: tr.setTreeRenderSupport(new TagTreeRenderSupport(this ));
0919: tr.render(writer, treeRoot, 0, extraAttrs, _iState);
0920: if (hasErrors()) {
0921: reportErrors();
0922: return;
0923: }
0924:
0925: // finish the tree representation and write it
0926: divRenderer.doEndTag(writer);
0927: sb.append("\n");
0928: write(sb.toString());
0929:
0930: if (!(treeRoot instanceof ITreeRootElement)) {
0931: boolean error = false;
0932: if (_rootNodeExpandedImage != null) {
0933: String s = Bundle.getString("Tags_TreeRootImageError",
0934: null);
0935: registerTagError(s, null);
0936: error = true;
0937: }
0938: if (_rootNodeCollapsedImage != null) {
0939: String s = Bundle.getString("Tags_TreeRootImageError",
0940: null);
0941: registerTagError(s, null);
0942: error = true;
0943: }
0944: if (error) {
0945: reportErrors();
0946: }
0947: }
0948:
0949: // check to see if we are writing out the java.
0950: if (_renderTagIdLookup) {
0951: String jsOut = renderDefaultJavaScript(request, divId);
0952: if (jsOut != null)
0953: write(jsOut);
0954: }
0955: }
0956:
0957: /**
0958: * Return the <code>TreeControl</code> instance for the tree control that
0959: * we are rendering.
0960: * @throws JspException if no TreeControl instance can be found
0961: */
0962: protected TreeElement getTreeRoot(ExpressionHandling expr)
0963: throws JspException {
0964: String datasource = "{" + _dataSource + "}";
0965: Object treeNode = expr.evaluateExpression(datasource,
0966: "dataSource", getPageContext());
0967: if (treeNode == null || hasErrors()) {
0968: return null;
0969: }
0970:
0971: if (!(treeNode instanceof TreeElement)) {
0972: String s = Bundle.getString("Tags_TreeInvalidAttribute",
0973: _dataSource);
0974: registerTagError(s, null);
0975: return null;
0976: }
0977: return (TreeElement) treeNode;
0978: }
0979:
0980: /**
0981: * Replace any occurrence of the specified placeholder in the specified
0982: * template string with the specified replacement value.
0983: * @param template Pattern string possibly containing the placeholder
0984: * @param placeholder Placeholder expression to be replaced
0985: * @param value Replacement value for the placeholder
0986: */
0987: protected String replace(String template, String placeholder,
0988: String value) {
0989: if (template == null)
0990: return null;
0991: if ((placeholder == null) || (value == null))
0992: return template;
0993:
0994: while (true) {
0995: int index = template.indexOf(placeholder);
0996: if (index < 0)
0997: break;
0998: InternalStringBuilder temp = new InternalStringBuilder(
0999: template.substring(0, index));
1000: temp.append(value);
1001: temp.append(template
1002: .substring(index + placeholder.length()));
1003: template = temp.toString();
1004: }
1005: return template;
1006: }
1007:
1008: /**
1009: * This method will handle creating the tagId attribute. The tagId attribute indentifies the
1010: * tag in the generated HTML. There is a lookup table created in JavaScript mapping the <coe>tagId</code>
1011: * to the actual name. The tagId is also run through the naming service so it can be scoped. Some tags will
1012: * write that <code>tagid</code> out as the <code>id</code> attribute of the HTML tag being generated.
1013: * @param tagId
1014: * @param state
1015: * @return String
1016: */
1017: protected final String renderTagId(HttpServletRequest request,
1018: String tagId, AbstractHtmlState state) {
1019: assert (_trs.tagId != null);
1020: state.id = getIdForTagId(tagId);
1021: String script = renderDefaultJavaScript(request, state.id);
1022: return script;
1023: }
1024:
1025: /**
1026: * This method will report all collected errors.
1027: * @param error
1028: */
1029: public void collectChildError(String error) {
1030: if (_errorText == null) {
1031: _errorText = new InternalStringBuilder(32);
1032: }
1033: _errorText.append(error);
1034: }
1035:
1036: //****************************
1037: /**
1038: * Much of the code below is taken from the HtmlBaseTag. We need to eliminate this duplication
1039: * through some type of helper methods.
1040: */
1041: private String renderDefaultJavaScript(HttpServletRequest request,
1042: String realId) {
1043: String idScript = null;
1044:
1045: // map the tagId to the real id
1046: if (TagConfig.isDefaultJavaScript()) {
1047: ScriptRequestState srs = ScriptRequestState
1048: .getScriptRequestState(request);
1049: idScript = srs.mapTagId(getScriptReporter(), _trs.tagId,
1050: realId, null);
1051: }
1052: return idScript;
1053: }
1054: }
|