1 18 package org.apache.beehive.netui.tags.tree; 19 20 import org.apache.beehive.netui.util.internal.InternalStringBuilder; 21 22 import org.apache.beehive.netui.core.URLCodec; 23 import org.apache.beehive.netui.pageflow.PageFlowUtils; 24 import org.apache.beehive.netui.pageflow.internal.AdapterManager; 25 import org.apache.beehive.netui.pageflow.internal.InternalUtils; 26 import org.apache.beehive.netui.pageflow.scoping.ScopedServletUtils; 27 import org.apache.beehive.netui.tags.HtmlUtils; 28 import org.apache.beehive.netui.tags.html.HtmlConstants; 29 import org.apache.beehive.netui.tags.internal.PageFlowTagUtils; 30 import org.apache.beehive.netui.tags.javascript.ScriptRequestState; 31 import org.apache.beehive.netui.tags.rendering.*; 32 import org.apache.beehive.netui.util.Bundle; 33 34 import javax.servlet.ServletContext ; 35 import javax.servlet.http.HttpServletRequest ; 36 import javax.servlet.http.HttpServletResponse ; 37 import javax.servlet.jsp.JspException ; 38 import java.io.IOException ; 39 import java.net.URISyntaxException ; 40 import java.util.HashMap ; 41 42 abstract public class TreeRenderer implements HtmlConstants 43 { 44 private TagRenderingBase _imageRenderer; 45 private TagRenderingBase _anchorRenderer; 46 private TagRenderingBase _divRenderer; 47 private TagRenderingBase _spanRenderer; 48 49 private TreeRenderState _trs; 50 51 private ImageTag.State _imgState = new ImageTag.State(); 52 private AnchorTag.State _anchorState = new AnchorTag.State(); 53 private DivTag.State _divState = new DivTag.State(); 54 private SpanTag.State _spanState = new SpanTag.State(); 55 56 private ServletContext _servletContext; 57 private HttpServletRequest _req; 58 private HttpServletResponse _res; 59 60 TreeRenderer(TreeRenderState trs, HttpServletRequest request, 61 HttpServletResponse response, ServletContext servletContext) 62 { 63 _trs = trs; 64 _imageRenderer = TagRenderingBase.Factory.getRendering(TagRenderingBase.IMAGE_TAG, request); 65 _anchorRenderer = TagRenderingBase.Factory.getRendering(TagRenderingBase.ANCHOR_TAG, request); 66 _divRenderer = TagRenderingBase.Factory.getRendering(TagRenderingBase.DIV_TAG, request); 67 _spanRenderer = TagRenderingBase.Factory.getRendering(TagRenderingBase.SPAN_TAG, request); 68 _servletContext = servletContext; 69 _req = request; 70 _res = response; 71 } 72 73 abstract protected void registerTagError(String message, Throwable e) 74 throws JspException ; 75 76 abstract protected String renderTagId(HttpServletRequest request, String tagId, AbstractHtmlState state); 77 78 protected void renderBeforeNode(AbstractRenderAppender writer, TreeElement node) 79 { 80 } 81 82 protected void renderAfterNode(AbstractRenderAppender writer, TreeElement node) 83 { 84 } 85 86 95 protected void render(InternalStringBuilder sb, TreeElement node, int level, AttributeRenderer attrs, 96 InheritableState state) 97 throws JspException 98 { 99 assert(sb != null); 101 assert(node != null); 102 103 String encoding = _res.getCharacterEncoding(); 104 String nodeName = node.getName(); 105 106 assert(nodeName != null); 107 108 String encodedNodeName = null; 113 try { 114 encodedNodeName = URLCodec.encode(nodeName, encoding); 115 assert(encodedNodeName != null); 116 } 117 catch (IOException e) { 118 String s = Bundle.getString("Tags_TreeEncodingError", null); 120 registerTagError(s, e); 121 return; 122 } 123 124 AttributeRenderer.RemoveInfo removes = attrs.addElement(node); 126 127 InternalStringBuilder img = new InternalStringBuilder(32); 129 130 131 _divState.clear(); 133 String tagId = node.getTagId(); 134 String script = null; 135 if (tagId != null) { 136 script = renderTagId(_req, tagId, _divState); 137 } 138 attrs.renderDiv(_divState, node); 139 if (_trs.runAtClient) { 140 _divState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_LEVEL, Integer.toString(level)); 141 } 142 sb.append(" "); 143 StringBuilderRenderAppender writer = new StringBuilderRenderAppender(sb); 144 renderBeforeNode(writer, node); 145 _divRenderer.doStartTag(writer, _divState); 146 sb.append("\n"); 147 if (script != null) 148 sb.append(script); 149 150 ServletContext servletContext = InternalUtils.getServletContext(_req); 153 boolean devMode = !AdapterManager.getServletContainerAdapter(servletContext).isInProductionMode(); 154 if (devMode) { 155 boolean error = false; 156 InternalStringBuilder errorText = new InternalStringBuilder(64); 157 if (node.getName() == null) { 158 errorText.append("name"); 159 error = true; 160 } 161 if (node.getParent() == null) { 162 if (error) 163 errorText.append(", "); 164 errorText.append("parent"); 165 } 166 167 if (error) 168 registerTagError(Bundle.getString("Tags_TreeStructureError", errorText.toString()), null); 169 } 170 171 InheritableState is = node.getInheritableState(); 175 if (is != null && is != state) { 176 is.setParent(state); 177 state = is; 178 } 179 180 _imgState.clear(); 184 _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, WIDTH, "16px"); 185 _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, BORDER, "0"); 186 _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, ALT, "", false); 187 for (int i = 0; i < level; i++) { 188 int levels = level - i; 189 TreeElement parent = node; 190 for (int j = 1; j <= levels; j++) 191 parent = parent.getParent(); 192 193 img.setLength(0); 194 img.append(state.getImageRoot()); 195 img.append('/'); 196 if (parent.isLast()) { 197 img.append(state.getImageSpacer()); 198 _imgState.style = null; 199 } 200 else { 201 img.append(state.getVerticalLineImage()); 202 _imgState.style = "vertical-align:bottom;"; 203 } 204 sb.append(" "); 205 _imgState.src = img.toString(); 206 _imageRenderer.doStartTag(writer, _imgState); 207 _imageRenderer.doEndTag(writer); 208 sb.append("\n"); 209 } 210 211 boolean closeAnchor = false; 213 if (!_trs.runAtClient) 214 closeAnchor = renderExpansionAnchor(sb, _anchorRenderer, node, nodeName, state); 215 else { 216 closeAnchor = renderClientExpansionAnchor(sb, _anchorRenderer, node, encodedNodeName, state); 218 } 219 220 String alt = ""; 223 img.setLength(0); 224 img.append(state.getImageRoot()); 225 img.append('/'); 226 if (node.isLeaf()) { if (node.isLast()) 228 img.append(state.getLastLineJoinImage()); 229 else 230 img.append(state.getLineJoinImage()); 231 } 232 else if (node.isExpanded()) { alt = Bundle.getString("Tags_TreeAltTextCollapse", null); 234 String rImg = null; 235 if (node.getParent() == null && node instanceof ITreeRootElement) { 236 rImg = ((ITreeRootElement) node).getRootNodeExpandedImage(); 237 if (rImg != null) { 238 img.append(rImg); 239 } 240 } 241 if (rImg == null) { 242 if (node.isLast()) 243 img.append(state.getLastNodeExpandedImage()); 244 else 245 img.append(state.getNodeExpandedImage()); 246 } 247 } 248 else { alt = Bundle.getString("Tags_TreeAltTextExpand", null); 250 String rImg = null; 251 if (node.getParent() == null && node instanceof ITreeRootElement) { 252 rImg = ((ITreeRootElement) node).getRootNodeCollapsedImage(); 253 if (rImg != null) { 254 img.append(rImg); 255 } 256 } 257 if (rImg == null) { 258 if (node.isLast()) 259 img.append(state.getLastNodeCollapsedImage()); 260 else 261 img.append(state.getNodeCollapsedImage()); 262 } 263 } 264 265 if (!closeAnchor) 267 sb.append(" "); 268 269 _imgState.clear(); 270 _imgState.src = img.toString(); 271 _imgState.style = "vertical-align:bottom;"; 272 _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, BORDER, "0"); 273 _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, ALT, alt, false); 274 275 _imageRenderer.doStartTag(writer, _imgState); 276 _imageRenderer.doEndTag(writer); 277 278 if (closeAnchor) 280 _anchorRenderer.doEndTag(writer); 281 sb.append("\n"); 282 283 String selectionLink = null; 286 if (!node.isDisabled()) { 287 288 String action = node.getAction(); 292 if (action == null) { 293 selectionLink = node.getHref(); 294 if (selectionLink == null && node.getClientAction() != null) 295 selectionLink = ""; 296 if (selectionLink == null) 297 action = state.getSelectionAction(); 298 } 299 300 if (action != null && selectionLink == null) { 302 303 HashMap params = null; 304 boolean remove = false; 305 params = node.getParams(); 306 if (params == null) { 307 params = new HashMap (); 308 remove = true; 309 } 310 params.put(TreeElement.SELECTED_NODE, nodeName); 311 if (_trs.tagId != null) { 312 params.put(TreeElement.TREE_ID, _trs.tagId); 313 } 314 315 String scope = node.getScope(); 317 if (scope != null) { 318 params.put(ScopedServletUtils.SCOPE_ID_PARAM, scope); 319 } 320 321 String uri = null; 322 try { 323 boolean xml = TagRenderingBase.Factory.isXHTML(_req); 324 uri = PageFlowUtils.getRewrittenActionURI(_servletContext, _req, _res, action, params, null, xml); 325 } 326 catch (URISyntaxException e) { 327 String s = Bundle.getString("Tags_Tree_Node_URLException", 329 new Object []{action, e.getMessage()}); 330 registerTagError(s, e); 331 } 332 333 if (remove) { 334 params.remove(TreeElement.SELECTED_NODE); 335 if (_trs.tagId != null) { 336 params.remove(TreeElement.TREE_ID); 337 } 338 339 if (scope != null) { 340 params.remove(ScopedServletUtils.SCOPE_ID_PARAM); 341 } 342 } 343 344 if (uri != null) { 345 selectionLink = _res.encodeURL(uri); 346 } 347 } 348 } 349 350 TagRenderingBase endRender = null; 351 if (selectionLink != null) { 353 _anchorState.clear(); 354 _anchorState.href = selectionLink; 355 String target = node.getTarget(); 356 if (target == null) { 357 target = state.getSelectionTarget(); 358 } 359 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TARGET, target); 360 String title = node.getTitle(); 361 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TITLE, title); 362 363 if (node.isSelected()) { 365 _anchorState.style = _trs.selectedStyle; 366 _anchorState.styleClass = _trs.selectedStyleClass; 367 } 368 else { 369 _anchorState.style = _trs.unselectedStyle; 370 _anchorState.styleClass = _trs.unselectedStyleClass; 371 } 372 if (_anchorState.style == null && _anchorState.styleClass == null) { 373 _anchorState.style = "text-decoration: none"; 374 } 375 376 attrs.renderSelectionLink(_anchorState, node); 378 379 if (_trs.runAtClient) { 381 String action = node.getClientAction(); 382 if (action != null) { 383 action = HtmlUtils.escapeEscapes(action); 384 action = ScriptRequestState.getString("netuiAction", new Object []{action}); 385 } 386 _anchorState.registerAttribute(AbstractHtmlState.ATTR_JAVASCRIPT, ONCLICK, action); 387 } 390 391 sb.append(" "); 393 _anchorRenderer.doStartTag(writer, _anchorState); 394 endRender = _anchorRenderer; 395 } 396 else { 397 _spanState.clear(); 400 _spanState.styleClass = _trs.disabledStyleClass; 401 _spanState.style = _trs.disabledStyle; 402 sb.append(" "); 403 _spanRenderer.doStartTag(writer, _spanState); 404 endRender = _spanRenderer; 405 } 406 sb.append(" "); 407 408 String icon = node.getIcon(); 411 if (icon == null) { 412 icon = state.getIconRoot() + "/" + state.getItemIcon(); 413 } 414 else { 415 icon = state.getIconRoot() + "/" + icon; 416 } 417 418 String label = node.getLabel(); 420 if (icon != null) { 421 _imgState.clear(); 422 _imgState.src = icon; 423 _imgState.style = "vertical-align:text-top"; 424 alt = null; 425 if (label != null && node.isLabelLegalAsAlt()) 426 alt = label; 427 else 428 alt = node.getTitle(); 429 if (alt == null) 430 alt = Bundle.getString("Tags_TreeAltText", null); 431 _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, ALT, alt, false); 432 _imgState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, BORDER, "0"); 433 434 attrs.renderIconImage(_imgState, node); 436 _imageRenderer.doStartTag(writer, _imgState); 437 _imageRenderer.doEndTag(writer); 438 sb.append(" "); 439 } 440 441 if (label != null) { 443 if (_trs.escapeContent) { 444 InternalStringBuilder s = new InternalStringBuilder(label.length() + 16); 445 StringBuilderRenderAppender sbAppend = new StringBuilderRenderAppender(sb); 446 HtmlUtils.filter(label, sbAppend); 447 label = s.toString(); 448 } 449 sb.append(label); 450 sb.append(" "); 451 } 452 endRender.doEndTag(writer); 453 454 String ctnt = node.getContent(); 456 if (ctnt != null) { 457 if (_trs.escapeContent) { 458 InternalStringBuilder s = new InternalStringBuilder(ctnt.length() + 16); 459 StringBuilderRenderAppender sbAppend = new StringBuilderRenderAppender(sb); 460 HtmlUtils.filter(ctnt, sbAppend); 461 ctnt = s.toString(); 462 } 463 sb.append("\n "); 464 sb.append(ctnt); 465 } 466 467 sb.append("\n "); 469 _divRenderer.doEndTag(writer); 470 sb.append("\n"); 471 renderAfterNode(writer, node); 472 473 475 attrs.removeElementScoped(node, removes); 476 if (node.isExpanded() || (_trs.runAtClient && !node.isExpandOnServer())) { 480 TreeElement children[] = node.getChildren(); 481 int newLevel = level + 1; 482 for (int i = 0; i < children.length; i++) { 483 render(sb, children[i], newLevel, attrs, state); 484 } 485 } 486 attrs.removeElement(node, removes); 487 } 488 489 private boolean renderExpansionAnchor(InternalStringBuilder sb, TagRenderingBase anchorRenderer, 490 TreeElement node, String nodeName, InheritableState state) 491 throws JspException 492 { 493 String action = state.getExpansionAction(); 495 if (action == null) { 496 action = state.getSelectionAction(); 497 } 498 boolean isAction = PageFlowTagUtils.isAction(_req, action); 499 if (!isAction) { 500 registerTagError(Bundle.getString("Tags_BadAction", action), null); 501 return false; 502 } 503 504 HashMap params = new HashMap (); 506 params.put(TreeElement.EXPAND_NODE, nodeName); 507 assert (_trs.tagId != null); 508 params.put(TreeElement.TREE_ID, _trs.tagId); 509 String uri = null; 510 try { 511 boolean xml = TagRenderingBase.Factory.isXHTML(_req); 512 uri = PageFlowUtils.getRewrittenActionURI(_servletContext, _req, _res, action, params, null, xml); 513 } 514 catch (URISyntaxException e) { 515 String s = Bundle.getString("Tags_Tree_Node_URLException", 517 new Object []{action, e.getMessage()}); 518 registerTagError(s, e); 519 } 520 521 boolean ret = false; 522 if ((uri != null) && !node.isLeaf()) { 523 _anchorState.clear(); 524 _anchorState.href = _res.encodeURL(uri); 525 sb.append(" "); 526 StringBuilderRenderAppender writer = new StringBuilderRenderAppender(sb); 527 anchorRenderer.doStartTag(writer, _anchorState); 528 ret = true; 529 } 530 return ret; 531 } 532 533 539 private boolean renderClientExpansionAnchor(InternalStringBuilder sb, TagRenderingBase anchorRenderer, 540 TreeElement node, String encodedNodeName, 541 InheritableState state) 542 { 543 boolean imgOverride = (state != state.getParent() && state.getParent() != null) || 544 (node.getParent() == null); 545 546 if (!node.isLeaf()) { 547 boolean expanded = node.isExpanded(); 548 _anchorState.clear(); 549 _anchorState.href = ""; 550 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_ANCHOR, 551 (expanded ? TreeElement.TREE_EXPAND_STATE : TreeElement.TREE_COLLAPSE_STATE)); 552 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_ANCHOR_INIT, "true"); 553 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_ANCHOR_ID, encodedNodeName); 554 if (node.isLast()) { 555 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_NODE_LAST, "true"); 556 } 557 558 if (imgOverride) { 560 if (node.getParent() == null) { 561 String rootImg = ((ITreeRootElement) node).getRootNodeCollapsedImage(); 562 if (rootImg != null) 563 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_COLLAPSE_IMAGE, 564 state.getImageRoot() + "/" + rootImg); 565 else 566 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_COLLAPSE_IMAGE, 567 state.getImageRoot() + "/" + state.getLastNodeCollapsedImage()); 568 rootImg = ((ITreeRootElement) node).getRootNodeExpandedImage(); 569 if (rootImg != null) 570 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_IMAGE, 571 state.getImageRoot() + "/" + rootImg); 572 else 573 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_IMAGE, 574 state.getImageRoot() + "/" + state.getLastNodeExpandedImage()); 575 } 576 else if (node.isLast()) { 577 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_COLLAPSE_IMAGE, 578 state.getImageRoot() + "/" + state.getLastNodeCollapsedImage()); 579 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_IMAGE, 580 state.getImageRoot() + "/" + state.getLastNodeExpandedImage()); 581 } 582 else { 583 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_COLLAPSE_IMAGE, 584 state.getImageRoot() + "/" + state.getNodeCollapsedImage()); 585 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_IMAGE, 586 state.getImageRoot() + "/" + state.getNodeExpandedImage()); 587 } 588 } 589 590 if (node.isExpandOnServer() && !node.isExpanded()) { 591 String path = _req.getServletPath(); 592 int idx = path.lastIndexOf('/'); 593 if (idx != -1) { 594 path = path.substring(1, idx); 595 } 596 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND, "true"); 597 _anchorState.registerAttribute(AbstractHtmlState.ATTR_GENERAL, TreeElement.TREE_EXPAND_PATH, path); 598 } 599 600 sb.append(" "); 601 StringBuilderRenderAppender writer = new StringBuilderRenderAppender(sb); 602 anchorRenderer.doStartTag(writer, _anchorState); 603 return true; 604 } 605 return false; 606 } 607 } 608 | Popular Tags |