1 16 17 package org.apache.myfaces.custom.tree2; 18 19 20 import org.apache.myfaces.component.html.util.AddResource; 21 import org.apache.myfaces.renderkit.html.HtmlRendererUtils; 22 import org.apache.myfaces.renderkit.html.HTML; 23 import org.apache.myfaces.renderkit.JSFAttr; 24 25 import javax.faces.component.NamingContainer; 26 import javax.faces.component.UIComponent; 27 import javax.faces.component.UICommand; 28 import javax.faces.component.UIGraphic; 29 import javax.faces.component.UIViewRoot; 30 import javax.faces.component.UIParameter; 31 import javax.faces.context.FacesContext; 32 import javax.faces.context.ResponseWriter; 33 import javax.faces.render.Renderer; 34 35 import java.io.IOException ; 36 import java.io.UnsupportedEncodingException ; 37 import java.util.List ; 38 import java.util.Map ; 39 import java.util.Iterator ; 40 import java.net.URLDecoder ; 41 import javax.servlet.http.Cookie ; 42 import java.util.HashMap ; 43 44 50 public class HtmlTreeRenderer extends Renderer 51 { 52 protected static final String TOGGLE_SPAN = "org.apache.myfaces.tree.TOGGLE_SPAN"; 53 protected static final String ROOT_NODE_ID = "0"; 54 55 private static final String JAVASCRIPT_ENCODED = "org.apache.myfaces.tree.JAVASCRIPT_ENCODED"; 56 private static final String NAV_COMMAND = "org.apache.myfaces.tree.NAV_COMMAND"; 57 private static final String ENCODING = "UTF-8"; 58 private static final String ATTRIB_DELIM = ";"; 59 private static final String ATTRIB_KEYVAL = "="; 60 private static final String NODE_STATE_EXPANDED = "x"; 61 private static final String NODE_STATE_CLOSED = "c"; 62 private final static String SEPARATOR = String.valueOf(NamingContainer.SEPARATOR_CHAR); 63 64 private static final int NOTHING = 0; 65 private static final int CHILDREN = 1; 66 private static final int EXPANDED = 2; 67 private static final int LINES = 4; 68 private static final int LAST = 8; 69 70 public boolean getRendersChildren() 72 { 73 return true; 74 } 75 76 public void decode(FacesContext context, UIComponent component) 77 { 78 super.decode(context, component); 79 80 String nodeId = null; 82 HtmlTree tree = (HtmlTree)component; 83 String originalNodeId = tree.getNodeId(); 84 85 if (getBoolean(component, JSFAttr.CLIENT_SIDE_TOGGLE, true)) 86 { 87 Map cookieMap = context.getExternalContext().getRequestCookieMap(); 88 Cookie treeCookie = (Cookie )cookieMap.get(component.getId()); 89 if (treeCookie == null || treeCookie.getValue() == null) 90 { 91 return; 92 } 93 94 String nodeState = null; 95 Map attrMap = getCookieAttr(treeCookie); 96 Iterator i = attrMap.keySet().iterator(); 97 while (i.hasNext()) 98 { 99 nodeId = (String )i.next(); 100 nodeState = (String )attrMap.get(nodeId); 101 102 if (NODE_STATE_EXPANDED.equals(nodeState)) 103 { 104 tree.setNodeId(nodeId); 105 if (!tree.isNodeExpanded()) 106 { 107 tree.toggleExpanded(); 108 } 109 tree.setNodeId(originalNodeId); 110 } 111 else if (NODE_STATE_CLOSED.equals(nodeState)) 112 { 113 tree.setNodeId(nodeId); 114 if (tree.isNodeExpanded()) 115 { 116 tree.toggleExpanded(); 117 } 118 tree.setNodeId(originalNodeId); 119 } 120 } 121 } 122 else 123 { 124 nodeId = (String )context.getExternalContext().getRequestParameterMap().get(tree.getId() + SEPARATOR + NAV_COMMAND); 125 126 if (nodeId == null || nodeId.equals("")) 127 { 128 return; 129 } 130 131 tree.setNodeId(nodeId); 132 tree.toggleExpanded(); 133 tree.setNodeId(originalNodeId); 134 } 135 } 136 137 public void encodeBegin(FacesContext context, UIComponent component) throws IOException 138 { 139 encodeJavascript(context, component); 141 } 142 143 152 public void encodeChildren(FacesContext context, UIComponent component) throws IOException 153 { 154 HtmlTree tree = (HtmlTree)component; 155 boolean showRootNode = getBoolean(tree, JSFAttr.SHOW_ROOT_NODE, true); 156 157 if (!component.isRendered()) return; 158 159 if (tree.getValue() == null) return; 160 161 ResponseWriter out = context.getResponseWriter(); 162 String clientId = null; 163 164 if (component.getId() != null && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX)) 165 { 166 clientId = component.getClientId(context); 167 } 168 169 boolean isOuterSpanUsed = false; 170 171 if (clientId != null) 172 { 173 isOuterSpanUsed = true; 174 out.startElement("span", component); 175 out.writeAttribute("id", clientId, "id"); 176 } 177 178 if (showRootNode) 179 { 180 encodeTree(context, out, tree, null, 0); 182 } 183 else 184 { 185 tree.setNodeId("0"); 186 TreeNode rootNode = tree.getNode(); 187 List rootChildren = rootNode.getChildren(); 188 int kidId = 0; 189 190 for (int i = 0; i < rootChildren.size(); i++) 191 { 192 encodeTree(context, out, tree, ROOT_NODE_ID, kidId++); 193 } 194 } 195 196 tree.setNodeId(null); 197 198 if (isOuterSpanUsed) 199 { 200 out.endElement("span"); 201 } 202 } 203 204 215 protected void encodeTree(FacesContext context, ResponseWriter out, HtmlTree tree, String parentId, int childCount) 216 throws IOException 217 { 218 boolean clientSideToggle = getBoolean(tree, JSFAttr.CLIENT_SIDE_TOGGLE, true); 219 220 String nodeId = (parentId != null) ? parentId + NamingContainer.SEPARATOR_CHAR + childCount : ROOT_NODE_ID; 221 String spanId = TOGGLE_SPAN + ":" + tree.getId() + ":" + nodeId; 222 223 tree.setNodeId(nodeId); 224 TreeNode node = tree.getNode(); 225 226 HtmlRendererUtils.writePrettyLineSeparator(context); 228 beforeNodeEncode(context, out, tree); 229 encodeCurrentNode(context, out, tree); 230 afterNodeEncode(context, out); 231 232 if (clientSideToggle == true || tree.isNodeExpanded()) 234 { 235 int kidId = 0; 236 String currId = tree.getNodeId(); 237 List children = node.getChildren(); 238 239 if (clientSideToggle) 241 { 242 out.startElement(HTML.SPAN_ELEM, tree); 243 out.writeAttribute(HTML.ID_ATTR, spanId, null); 244 245 if (tree.isNodeExpanded()) 246 { 247 out.writeAttribute(HTML.STYLE_ATTR, "display:block", null); 248 } 249 else 250 { 251 out.writeAttribute(HTML.STYLE_ATTR, "display:none", null); 252 } 253 } 254 255 for (int i = 0; i < children.size(); i++) 256 { 257 encodeTree(context, out, tree, currId, kidId++); 258 } 259 260 if (clientSideToggle) 261 { 262 out.endElement(HTML.SPAN_ELEM); 263 } 264 } 265 } 266 267 276 protected void encodeCurrentNode(FacesContext context, ResponseWriter out, HtmlTree tree) 277 throws IOException 278 { 279 TreeNode node = tree.getNode(); 280 281 boolean showRootNode = getBoolean(tree, JSFAttr.SHOW_ROOT_NODE, true); 283 boolean showNav = getBoolean(tree, JSFAttr.SHOW_NAV, true); 284 boolean showLines = getBoolean(tree, JSFAttr.SHOW_LINES, true); 285 boolean clientSideToggle = getBoolean(tree, JSFAttr.CLIENT_SIDE_TOGGLE, true); 286 287 if (clientSideToggle) 288 { 289 showNav = true; 291 } 292 293 UIComponent nodeTypeFacet = tree.getFacet(node.getType()); 294 UIComponent nodeImgFacet = null; 295 296 if (nodeTypeFacet == null) 297 { 298 throw new IllegalArgumentException ("Unable to locate facet with the name: " + node.getType()); 299 } 300 301 String [] pathInfo = tree.getPathInformation(tree.getNodeId()); 303 int paddingLevel = pathInfo.length - 1; 304 305 for (int i = (showRootNode ? 0 : 1); i < paddingLevel; i++) 306 { 307 boolean lastChild = tree.isLastChild((String )pathInfo[i]); 308 String lineSrc = (!lastChild && showLines) 309 ? getImageSrc(context, tree, "line-trunk.gif") 310 : getImageSrc(context, tree, "spacer.gif"); 311 312 out.startElement(HTML.TD_ELEM, tree); 313 out.writeAttribute(HTML.WIDTH_ATTR, "19", null); 314 out.writeAttribute(HTML.HEIGHT_ATTR, "100%", null); 315 out.writeURIAttribute("background", lineSrc, null); 316 out.startElement(HTML.IMG_ELEM, tree); 317 out.writeURIAttribute(HTML.SRC_ATTR, lineSrc, null); 318 out.writeAttribute(HTML.WIDTH_ATTR, "19", null); 319 out.writeAttribute(HTML.HEIGHT_ATTR, "18", null); 320 out.writeAttribute(HTML.BORDER_ATTR, "0", null); 321 out.endElement(HTML.IMG_ELEM); 322 out.endElement(HTML.TD_ELEM); 323 } 324 325 if (showNav) 326 { 327 nodeImgFacet = encodeNavigation(context, out, tree); 328 } 329 330 out.startElement(HTML.TD_ELEM, tree); 332 if (nodeImgFacet != null) 333 { 334 encodeRecursive(context, nodeImgFacet); 335 } 336 encodeRecursive(context, nodeTypeFacet); 337 out.endElement(HTML.TD_ELEM); 338 } 339 340 protected void beforeNodeEncode(FacesContext context, ResponseWriter out, HtmlTree tree) 341 throws IOException 342 { 343 out.startElement(HTML.TABLE_ELEM, null); 344 out.writeAttribute(HTML.CELLPADDING_ATTR, "0", null); 345 out.writeAttribute(HTML.CELLSPACING_ATTR, "0", null); 346 out.writeAttribute(HTML.BORDER_ATTR, "0", null); 347 out.startElement(HTML.TR_ELEM, null); 348 } 349 350 protected void afterNodeEncode(FacesContext context, ResponseWriter out) 351 throws IOException 352 { 353 out.endElement(HTML.TR_ELEM); 354 out.endElement(HTML.TABLE_ELEM); 355 } 356 357 366 private UIComponent encodeNavigation(FacesContext context, ResponseWriter out, HtmlTree tree) 367 throws IOException 368 { 369 TreeNode node = tree.getNode(); 370 String nodeId = tree.getNodeId(); 371 String spanId = TOGGLE_SPAN + ":" + tree.getId() + ":" + nodeId; boolean showLines = getBoolean(tree, JSFAttr.SHOW_LINES, true); 373 boolean clientSideToggle = getBoolean(tree, JSFAttr.CLIENT_SIDE_TOGGLE, true); 374 UIComponent nodeTypeFacet = tree.getFacet(node.getType()); 375 String navSrc = null; 376 String altSrc = null; 377 UIComponent nodeImgFacet = null; 378 379 int bitMask = NOTHING; 380 bitMask += (node.getChildCount()>0) ? CHILDREN : NOTHING; 381 bitMask += (tree.isNodeExpanded()) ? EXPANDED : NOTHING; 382 bitMask += (tree.isLastChild(tree.getNodeId())) ? LAST : NOTHING; 383 bitMask += (showLines) ? LINES : NOTHING; 384 385 switch (bitMask) 386 { 387 case (NOTHING): 388 389 case (LAST): 390 navSrc = "spacer.gif"; 391 break; 392 393 case (LINES): 394 navSrc = "line-middle.gif"; 395 break; 396 397 case (LINES + LAST): 398 navSrc = "line-last.gif"; 399 break; 400 401 case (CHILDREN): 402 403 case (CHILDREN + LAST): 404 navSrc = "nav-plus.gif"; 405 altSrc = "nav-minus.gif"; 406 break; 407 408 case (CHILDREN + LINES): 409 410 navSrc = "nav-plus-line-middle.gif"; 411 altSrc = "nav-minus-line-middle.gif"; 412 break; 413 414 case (CHILDREN + LINES + LAST): 415 416 navSrc = "nav-plus-line-last.gif"; 417 altSrc = "nav-minus-line-last.gif"; 418 break; 419 420 case (CHILDREN + EXPANDED): 421 422 case (CHILDREN + EXPANDED + LAST): 423 navSrc = "nav-minus.gif"; 424 altSrc = "nav-plus.gif"; 425 break; 426 427 case (CHILDREN + EXPANDED + LINES): 428 navSrc = "nav-minus-line-middle.gif"; 429 altSrc = "nav-plus-line-middle.gif"; 430 break; 431 432 case (CHILDREN + EXPANDED + LINES + LAST): 433 navSrc = "nav-minus-line-last.gif"; 434 altSrc = "nav-plus-line-last.gif"; 435 break; 436 437 default: 438 throw new IllegalArgumentException ("Invalid bit mask of " + bitMask); 439 } 440 441 String navSrcUrl = getImageSrc(null, tree, navSrc); 443 navSrc = getImageSrc(context, tree, navSrc); 444 altSrc = getImageSrc(context, tree, altSrc); 445 446 out.startElement(HTML.TD_ELEM, tree); 448 out.writeAttribute(HTML.WIDTH_ATTR, "19", null); 449 out.writeAttribute(HTML.HEIGHT_ATTR, "100%", null); 450 out.writeAttribute("valign", "top", null); 451 452 if ((bitMask & LINES)!=0 && (bitMask & LAST)==0) 453 { 454 out.writeURIAttribute("background", getImageSrc(context, tree, "line-trunk.gif"), null); 455 } 456 457 UIGraphic image = new UIGraphic(); 459 image.setId(context.getViewRoot().createUniqueId()); 460 image.setUrl(navSrcUrl); 461 Map imageAttrs = image.getAttributes(); 462 imageAttrs.put(HTML.WIDTH_ATTR, "19"); 463 imageAttrs.put(HTML.HEIGHT_ATTR, "18"); 464 imageAttrs.put(HTML.BORDER_ATTR, "0"); 465 466 if (clientSideToggle) 467 { 468 472 String expandImgSrc = ""; 473 String collapseImgSrc = ""; 474 String nodeImageId = ""; 475 476 UIComponent expandFacet = nodeTypeFacet.getFacet("expand"); 477 if (expandFacet != null) 478 { 479 UIGraphic expandImg = (UIGraphic)expandFacet; 480 expandImgSrc = expandImg.getUrl(); 481 if (expandImg.isRendered()) 482 { 483 expandImg.setId(context.getViewRoot().createUniqueId()); 484 nodeImageId = expandImg.getClientId(context); 485 nodeImgFacet = expandFacet; 486 } 487 } 488 489 UIComponent collapseFacet = nodeTypeFacet.getFacet("collapse"); 490 if (collapseFacet != null) 491 { 492 UIGraphic collapseImg = (UIGraphic)collapseFacet; 493 collapseImgSrc = collapseImg.getUrl(); 494 if (collapseImg.isRendered()) 495 { 496 collapseImg.setId(context.getViewRoot().createUniqueId()); 497 nodeImageId = collapseImg.getClientId(context); 498 nodeImgFacet = collapseFacet; 499 } 500 } 501 502 if (node.getChildCount() > 0) 503 { 504 String onClick = new StringBuffer () 505 .append("treeNavClick('") 506 .append(spanId) 507 .append("', '") 508 .append(image.getClientId(context)) 509 .append("', '") 510 .append(navSrc) 511 .append("', '") 512 .append(altSrc) 513 .append("', '") 514 .append(nodeImageId) 515 .append("', '") 516 .append(expandImgSrc) 517 .append("', '") 518 .append(collapseImgSrc) 519 .append("', '") 520 .append(tree.getId()) 521 .append("', '") 522 .append(nodeId) 523 .append("');") 524 .toString(); 525 526 imageAttrs.put(HTML.ONCLICK_ATTR, onClick); 527 imageAttrs.put(HTML.STYLE_ATTR, "cursor:hand;cursor:pointer"); 528 } 529 encodeRecursive(context, image); 530 } 531 else 532 { 533 UICommand expandControl = tree.getExpandControl(); 535 expandControl.setId(context.getViewRoot().createUniqueId()); 536 expandControl.getChildren().clear(); 537 538 UIParameter param = new UIParameter(); 539 param.setName(tree.getId() + NamingContainer.SEPARATOR_CHAR + NAV_COMMAND); 540 param.setValue(tree.getNodeId()); 541 expandControl.getChildren().add(param); 542 expandControl.getChildren().add(image); 543 544 encodeRecursive(context, expandControl); 545 } 546 out.endElement(HTML.TD_ELEM); 547 548 return nodeImgFacet; 549 } 550 551 private void encodeRecursive(FacesContext context, UIComponent component) throws IOException 552 { 553 554 if (!component.isRendered()) return; 555 556 component.encodeBegin(context); 557 558 if (component.getRendersChildren()) 559 { 560 component.encodeChildren(context); 561 } 562 else 563 { 564 List childList = component.getChildren(); 565 566 for (int i=0; i < childList.size(); i++) 567 { 568 UIComponent child = (UIComponent)childList.get(i); 569 encodeRecursive(context, child); 570 } 571 } 572 573 component.encodeEnd(context); 574 } 575 576 584 private void encodeJavascript(FacesContext context, UIComponent component) throws IOException 585 { 586 if (context.getExternalContext().getRequestMap().containsKey(JAVASCRIPT_ENCODED)) 588 { 589 return; 590 } 591 592 ResponseWriter out = context.getResponseWriter(); 594 String javascriptLocation = (String )component.getAttributes().get(JSFAttr.JAVASCRIPT_LOCATION); 595 if (javascriptLocation == null) 596 { 597 AddResource.addJavaScriptHere(HtmlTreeRenderer.class, "javascript/tree.js", context); 598 AddResource.addJavaScriptHere(HtmlTreeRenderer.class, "javascript/cookielib.js", context); 599 } 600 else 601 { 602 out.startElement(HTML.SCRIPT_ELEM, null); 603 out.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null); 604 out.writeAttribute(HTML.SRC_ATTR, 605 javascriptLocation + "/tree.js", null); 606 out.endElement(HTML.SCRIPT_ELEM); 607 608 out.startElement(HTML.SCRIPT_ELEM, null); 609 out.writeAttribute(HTML.TYPE_ATTR, "text/javascript", null); 610 out.writeAttribute(HTML.SRC_ATTR, 611 javascriptLocation + "/cookielib.js", null); 612 out.endElement(HTML.SCRIPT_ELEM); 613 } 614 615 context.getExternalContext().getRequestMap().put(JAVASCRIPT_ENCODED, Boolean.TRUE); 616 } 617 618 628 private String getImageSrc(FacesContext context, UIComponent component, String imageName) 629 { 630 String imageLocation = (String )component.getAttributes().get(JSFAttr.IMAGE_LOCATION); 631 if (imageLocation == null) 632 { 633 return AddResource.getResourceMappedPath(HtmlTreeRenderer.class, 634 "images/" + imageName, context); 635 } 636 else 637 { 638 return imageLocation + "/" + imageName; 639 } 640 } 641 642 651 protected boolean getBoolean(UIComponent component, String attributeName, boolean defaultValue) 652 { 653 Boolean booleanAttr = (Boolean )component.getAttributes().get(attributeName); 654 655 if (booleanAttr == null) 656 { 657 return defaultValue; 658 } 659 else 660 { 661 return booleanAttr.booleanValue(); 662 } 663 } 664 665 private Map getCookieAttr(Cookie cookie) 666 { 667 Map attribMap = new HashMap (); 668 try 669 { 670 String cookieValue = URLDecoder.decode(cookie.getValue(),ENCODING); 671 String [] attribArray = cookieValue.split(ATTRIB_DELIM); 672 for (int j = 0; j < attribArray.length; j++) 673 { 674 int index = attribArray[j].indexOf(ATTRIB_KEYVAL); 675 String name = attribArray[j].substring(0, index); 676 String value = attribArray[j].substring(index + 1); 677 attribMap.put(name, value); 678 } 679 } 680 catch (UnsupportedEncodingException e) 681 { 682 throw new RuntimeException ("Error parsing tree cookies", e); 683 } 684 return attribMap; 685 } 686 } 687 | Popular Tags |