package org.freehep.webutil.tree; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.BitSet; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import javax.servlet.jsp.JspWriter; import org.freehep.webutil.util.properties.PropertiesLoader; /** * A class for rendering Trees as HTML * @author The FreeHEP team @ SLAC * */ public class Tree { private int n = 0; private IconSet iconSet; private boolean showRootNode = true; private boolean showItemCount = false; private String leafHref; private String folderHref; private int folderHrefLimit = -1; private String treeTarget = null; private String onclick = null; private Hashtable nodeStatus = new Hashtable(); private boolean useCookies = true; private boolean showEmptyFolders = false; private boolean showFolderHrefForNodesWithLeavesOnly = false; private String fhiconPath; // If leafHref or folderHref has this value, href=null is used public static String noHref = "none"; /** * Creates a Tree renderer using the standard IconSet and with the * icons located at the given path. */ public Tree(String path) { this.fhiconPath = path; iconSet = new DefaultIconSet(); } public Tree(IconSet icons) { iconSet = icons; } public boolean isShowFolderHrefForNodesWithLeavesOnly() { return showFolderHrefForNodesWithLeavesOnly; } public boolean getShowFolderHrefForNodesWithLeavesOnly() { return showFolderHrefForNodesWithLeavesOnly; } public void setShowFolderHrefForNodesWithLeavesOnly(boolean visible) { showFolderHrefForNodesWithLeavesOnly = visible; } public boolean isRootVisible() { return showRootNode; } public void setRootVisible(boolean visible) { showRootNode = visible; } public void setShowItemCount(boolean visible) { showItemCount = visible; } public void setLeafHref(String href) { this.leafHref = href; } public String getLeafHref() { return leafHref; } public void setFolderHrefLimit(int limit) { this.folderHrefLimit = limit; } public int getFolderHrefLimit() { return folderHrefLimit; } public void setFolderHref(String href) { this.folderHref = href; } public String getFolderHref() { return folderHref; } public void setTarget(String target) { this.treeTarget = target; } public String getTarget() { return treeTarget; } public void setOnclick(String onclick) { this.onclick = onclick; } public String getOnclick() { return onclick; } public void setUseCookies(boolean useCookies) { this.useCookies = useCookies; } public boolean getUseCookies() { return useCookies; } boolean getShowEmptyFolders() { return showEmptyFolders; } void setShowEmptyFolders(boolean showEmptyFolders) { this.showEmptyFolders = showEmptyFolders; } void setNodeStatus(TreeNode node, boolean isExpanded) { nodeStatus.put(node, new Boolean(isExpanded)); } private boolean isNodeExpanded(TreeNode node) { Object status = nodeStatus.get(node); if ( status == null ) return node.isExpanded(); else return ((Boolean) status).booleanValue(); } public void printTree(JspWriter out, TreeNode root, String title) throws IOException { out.println("<div class=\"directory\">"); if ((title == null || title.length() == 0) && !showRootNode) title = root.getLabel(); if (title != null && title.length()>0) out.println("<h3>"+title+"</h3>"); out.println("<div style=\"display: block;\">"); n = 0; BitSet isLast = new BitSet(); List children; if ( showRootNode ) { children = new ArrayList(); children.add(root); } else children = root.children(); if ( children.size() > 0 ) { int childrenLastNodeIndex = children.size() - 1; TreeNode lastNode = (TreeNode) children.get(childrenLastNodeIndex); if ( ! getShowEmptyFolders() ) { for ( int i = childrenLastNodeIndex; i > -1; i-- ) { TreeNode bottomNode = (TreeNode) children.get(i); boolean isBottomNode = bottomNode.isLeaf() ? true : TreeUtils.nodeHasInnerLeaves(bottomNode); if ( isBottomNode ) { lastNode = bottomNode; break; } } } for (Iterator i = children.iterator(); i.hasNext(); ) { TreeNode child = (TreeNode) i.next(); isLast.set(0,child == lastNode); listDirectory(out,0,child,"/"+child.getLabel(),isLast); } } out.println("</div>"); out.println("</div>"); } private int countInnerLeaves(TreeNode node) { return countInnerLeaves(node, true); } private int countInnerLeaves(TreeNode node, boolean recursive) { List children = node.children(); int count = 0; for ( int k = 0; k < children.size(); k++ ) { TreeNode child = (TreeNode) children.get(k); if ( child.isLeaf() ) count++; else if (recursive) count += countInnerLeaves(child); } return count; } private void listDirectory(JspWriter out, int depth, TreeNode node, String path, BitSet isLast) throws IOException { if ( ! node.isLeaf() && ! getShowEmptyFolders() && ! TreeUtils.nodeHasInnerLeaves(node) ) return; if ( node.isLeaf() ) out.print("<p class=\"leaf\">"); else out.print("<p class=\"folder\">"); for (int j=0; j<depth; j++) printIcon(out, isLast.get(j) ? iconSet.getBlank() : iconSet.getVerticalLine(), null); String label = node.getLabel(); int selfInnerLeaves = countInnerLeaves(node, false); int innerLeaves = countInnerLeaves(node, true); if (showItemCount && !node.isLeaf()) { if (selfInnerLeaves == innerLeaves) label += " ( "+innerLeaves+" )"; else label += " ( "+selfInnerLeaves+" / "+innerLeaves+" )"; } Icon icon = node.getIcon(); String href = node.isLeaf() ? leafHref : folderHref; if (href == null) href = node.getHref(); if ( (folderHrefLimit > 0 && innerLeaves > folderHrefLimit) || (!node.isLeaf() && showFolderHrefForNodesWithLeavesOnly && !TreeUtils.nodeHasInnerLeaves(node, false)) ) href = null; // Overwrite href for a special case if (node.getHref() == Tree.noHref) href = null; if (node.isLeaf()) { printIcon(out, isLast.get(depth) ? iconSet.getLastNode() : iconSet.getNode(), null); printIcon(out, icon == null ? iconSet.getDocument() : icon, null); } else { if ( isNodeExpanded(node) ) { printIcon(out, isLast.get(depth) ? iconSet.getLastMinus() : iconSet.getMinus(), "this"); printIcon(out, icon == null ? iconSet.getFolderOpen() : icon, "this.previousSibling"); } else { printIcon(out, isLast.get(depth) ? iconSet.getLastPlus() : iconSet.getPlus(), "this"); printIcon(out, icon == null ? iconSet.getFolderClosed() : icon, "this.previousSibling"); } } String title = node.getTitle(); title = title == null ? "" : "title=\""+title+"\""; //System.out.println(title+" :: label="+label+", node.getHref()="+node.getHref()+", href="+href); if (href != null) { href = href.replaceAll("%p", URLEncoder.encode(path,"UTF-8")); href = href.replaceAll("%l", URLEncoder.encode(node.getLabel(),"UTF-8")); String target = node.getTarget(); if ( target == null ) target = treeTarget; target = target == null ? "" : "target=\""+target+"\" "; out.println("<a href=\""+href+"\" "+target+" "+title+">"+label+"</a></p>"); } else out.println(label); if (!node.isLeaf()) { String folderDiv = "<div id=\"folder"+n+"\" title=\""+path+"\" "; if ( isNodeExpanded(node) ) folderDiv += " style=\"display: block;\""; folderDiv += ">"; out.println(folderDiv); n++; List children = node.children(); for (Iterator i = children.iterator(); i.hasNext(); ) { TreeNode child = (TreeNode) i.next(); isLast.set(depth+1,!i.hasNext()); String newPath = path+"/"+child.getLabel(); if ( newPath.startsWith("//") ) newPath = newPath.substring(1); listDirectory(out,depth+1,child,newPath,isLast); } out.println("</div>"); } } private void printIcon(JspWriter out, Icon icon, String toggle) throws IOException { if (icon != null) { out.print("<img src=\""); out.print(fhiconPath+"/"+icon.getSourceURL()); out.print("\" alt=\""); out.print(icon.getAlt()); out.print("\" width=\""); out.print(icon.getWidth()); out.print("\" height=\""); out.print(icon.getHeight()); if (toggle != null) out.print("\" onclick=\"toggleFolder('folder"+n+"',"+toggle+")"); out.print("\">"); } } public void printStyle(JspWriter out) throws IOException { out.println("<style type=\"text/css\">"); out.println("<!--"); out.println(".folder { "+PropertiesLoader.treeFolderStyle()+" }"); out.println(".leaf { "+PropertiesLoader.treeLeafStyle()+" }"); out.println(".directory h3 { margin: 0px; margin-top: 1em; font-size: 11pt; }"); out.println(".directory p { margin: 0px; white-space: nowrap; }"); out.println(".directory div { display: none; margin: 0px; }"); out.println(".directory img { vertical-align: middle; }"); out.println("-->"); out.println("</style>"); } public void printScript(JspWriter out) throws IOException { out.println("<script type=\"text/javascript\">"); out.println("<!-- // Hide script from old browsers"); out.println("function createFolderCookie(name,value,days) {"); out.println(" if (days != 0) {"); out.println(" var date = new Date();"); out.println(" date.setTime(date.getTime()+(days*24*60*60*1000));"); out.println(" expires = \"; expires=\"+date.toGMTString();"); out.println(" } else var expires = \"\";"); out.println(" document.cookie = \"tree.folder.\"+escape(name)+\"=\"+value+expires+\"; path=/\";"); out.println("}"); out.println(""); out.println("function eraseFolderCookie(name) {"); out.println(" createFolderCookie(name,\"\",0);"); out.println("}"); out.println(""); out.println("function toggleFolder(id, imageNode) {"); out.println(" var folder = document.getElementById(id);"); out.println(" var kkk = 0;"); out.println(" var l = 0;"); out.println(" if (folder == null) {"); out.println(" } "); out.println(" else if (folder.style.display == \"block\") {"); out.println(" if (imageNode != null) {"); out.println(" imageNode.nextSibling.src = \""+fhiconPath+"/"+iconSet.getFolderClosed().getSourceURL()+"\";"); out.println(" l = imageNode.src.length;"); out.println(" if (imageNode.src.substring(l-"+(fhiconPath+"/"+iconSet.getMinus().getSourceURL()).length()+",l) == \""+fhiconPath+"/"+iconSet.getMinus().getSourceURL()+"\") {"); out.println(" imageNode.src = \""+fhiconPath+"/"+iconSet.getPlus().getSourceURL()+"\";"); out.println(" } else {"); out.println(" imageNode.src = \""+fhiconPath+"/"+iconSet.getLastPlus().getSourceURL()+"\";"); out.println(" }"); out.println(" }"); out.println(" eraseFolderCookie(folder.title);"); out.println(" folder.style.display = \"none\";"); out.println(" } else {"); out.println(" if (imageNode != null) {"); out.println(" imageNode.nextSibling.src = \""+fhiconPath+"/"+iconSet.getFolderOpen().getSourceURL()+"\";"); out.println(" l = imageNode.src.length;"); out.println(" if (imageNode.src.substring(l-"+(fhiconPath+"/"+iconSet.getPlus().getSourceURL()).length()+",l) == \""+fhiconPath+"/"+iconSet.getPlus().getSourceURL()+"\") {"); out.println(" imageNode.src = \""+fhiconPath+"/"+iconSet.getMinus().getSourceURL()+"\";"); out.println(" } else {"); out.println(" imageNode.src = \""+fhiconPath+"/"+iconSet.getLastMinus().getSourceURL()+"\";"); out.println(" }"); out.println(" }"); out.println(" folder.style.display = \"block\";"); out.println(" createFolderCookie(folder.title,folder.style.display,15);"); out.println(" }"); // If a function is added to onclick it gets added here. if ( getOnclick() != null ) out.println(" "+getOnclick()); out.println("}"); out.println("// End script hiding -->"); out.println("</script>"); } }