|
磁盘文件、XML文档和SQL中的表(有一定要求)都可以生成目录树。 import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; /** * 目录树. <br> * 提供由其它对象生成目录树的支持。<br> * 如磁盘目录:Tree t=Tree.getTree(new java.io.File("D:/"),new FileContainer());<br> * XML文件:Document doc=...<br> * Tree t=Tree.getTree(doc,new DocumentContainer());<br> * 如果有File或Document以外的对象要生成目录树,请实现{@link TreeNode}或{@link Container}接口。 * 对于SQL中的表,建议字段要求id(标识)、parentId(上级目录标识),其它字段自定,然后对查询到的每一 * 条记录生成一个实现TreeNode接口的对象。 * 如果实现了TreeNode接口:<br> * class Group implements TreeNode{}<br> * Group[] groups=...<br> * Tree t=Tree.getTree(groups,0);//其中0为根结点的ID<br>,并且groups不要求是同一对象,只要它们都实现了 * 接口TreeNode,并且ID不同,这样可以在生成目录树的过程中根据对象类型自行处理。 * <p>Copyright: Copyright (c) 2004</p> * <p> </p> * @author flyxxxxx * @version 1.0 */ final public class Tree extends Node { /** * 结点最大标识 */ private static int maxId = 0; private Tree() { super(getMaxId(), null); } private Tree(int id) { super(id, null); maxId = id++; } private static int getMaxId() { return maxId++; } /** * 创建空目录树. * @return Tree 空目录树 */ public static Tree getTree() { return new Tree(); } /** * 向目录树添加一个结点. * 目录树中所有结点的类型最好是相同的。 * @param parent Node 父结点 * @param value Object 结点的值 * @return Node 添加的结点 */ public Node addNode(Node parent, Object value) { Node rs = new Node(getMaxId(), parent); rs.setValue(value); return rs; } /** * 创建目录树. * 如果对象实现了接口{@link Container},可以通过此方法加入目录树。<br> * 通过此方法创建的目录树,所有结点的ID由系统生成。obj将直接做为根结点,对根结点调用方法 * {@link Node#getValue()}将得到obj。<br> * @param obj Object 目录树的根结点 * @param container Container 得到对象的子对象的接口 * @return Tree 目录树 */ public static Tree getTree(Object obj, Container container) { Tree rs = new Tree(); rs.setValue(obj); Object[] o = container.getChilds(obj); for (int i = 0; i < o.length; i++) { addNode(rs, o[i], container); } return rs; } private static void addNode(Node n, Object obj, Container container) { Node node = new Node(getMaxId(), n); node.setValue(obj); Object[] o = container.getChilds(obj); for (int i = 0; i < o.length; i++) { addNode(node, o[i], container); } } /** * 创建目录树. * 如果对象实现了接口{@link Container},可以通过此方法加入目录树。<br> * 通过此方法创建的目录树,所有结点的ID由系统生成,对根结点调用方法{@link Node#getValue()} * 将得到null。<br> * obj数组中的每一个,将直接做为根结点的直接子结点。 * @param obj Object 目录树的根结点的直接子结点. * @param container Container 得到对象的子对象的接口 * @return Tree 目录树 */ public static Tree getTree(Object obj[], Container container) { Tree rs = new Tree(); for (int i = 0; i < obj.length; i++) { addNode(rs, obj[i], container); } return rs; } /** * 创建目录树. * 只要一组对象实现接口{@link TreeNode},并且每个对象的ID不同,就可以将它们加入目录树。<br> * 通过此方法得到的目录树,它的根结点ID值为rootId,其它结点的值为实现接口TreeNode的对象的ID。<br> * 如果treeNode中包含了根结点,根结点的值可以通过方法{@link Node#getValue()}得到,返之得到的是null。<br> * treeNode可以没有顺序,但父结点的ID一定大于子结点的。 * @param treeNode TreeNode[] 构成目录树的结点 * @param rootId int 根结点的ID * @return Tree 创建目录树 */ public static Tree getTree(TreeNode[] treeNode, int rootId) { Tree rs = new Tree(rootId); ArrayList list = new ArrayList(); for (int i = 0; i < treeNode.length; i++) { list.add(treeNode[i]); } Collections.sort(list, new Compare()); //排序 Node last = rs; for (int i = 0; i < treeNode.length; i++) { TreeNode tnode = (TreeNode) list.get(i); if (i == 0 && tnode.getId() == rootId) { //是否根结点 rs.setValue(tnode); } else { Node parent = null; //寻找父结点 if ( ( (TreeNode) last.getValue()).getId() == tnode.getParentId()) { parent = last; } else { parent = rs.getNode(tnode.getParentId()); } if (parent == null) { //未找到 throw new NullPointerException("Node " + tnode.getParentId() + " not found."); } else { //找到 Node n = new Node(tnode.getId(), parent); n.setValue(tnode); last = parent; } } } return rs; } /** * 从目录树中查找标识为id的结点. * @param id String 结点标识 * @return Node 标识为id的结点(未找到返回null) */ public Node getNode(int id) { if (id == getId()) { return this; } return getNode(getChilds(), id); } private static Node getNode(Iterator it, int id) { //查找结点 while (it.hasNext()) { Node n = (Node) it.next(); if (n.getId() == id) { return n; } if (n.getChildsNumber() > 0) { n = getNode(n.getChilds(), id); if (n != null) { return n; } } } return null; } /** * 对目录树进行排序 * @param com Comparator 排序接口 */ public void sort(Comparator com) { sort(childs, com); } private void sort(ArrayList childs, Comparator com) { //对子结点排序 Collections.sort(childs, com); for (int i = 0; i < childs.size(); i++) { Node n = (Node) childs.get(i); if (n.getChildsNumber() > 1) { sort(n.childs, com); } } } /** * 得到满足条件的结点列表. * @param filter NodeFilter 结点过滤器 * @return Iterator 结点列表(存储Node对象) */ public Iterator getNodeList(NodeFilter filter) { ArrayList rs = new ArrayList(); getNodeList(childs, filter, rs); return rs.iterator(); } private void getNodeList(ArrayList childs, NodeFilter filter, ArrayList rs) { //检索满足条件的结点 for (int i = 0; i < childs.size(); i++) { Node n = (Node) childs.get(i); if (filter.accept(n)) { rs.add(n); } if (n.hasChilds()) { getNodeList(n.childs, filter, rs); } } } } class Compare implements Comparator //对结点按ID排序 { public Compare() {} public int compare(Object obj1, Object obj2) { int id1 = ( (TreeNode) obj1).getId(); int id2 = ( (TreeNode) obj2).getId(); return id1 - id2; } }
import java.util.ArrayList; import java.util.Iterator; /** * 目录树的一个结点. <br> * 它的主要属性有结点标识、父结点、它在目录树中的层次(根结点为0)、结点的值、子结点。 * <p>Copyright: Copyright (c) 2004</p> * @author flyxxxxx * @version 1.0 */ public class Node { private int id; private Node parent; private int level; private Object value; protected ArrayList childs = new ArrayList(); /** * 构造方法 * @param id int 结点ID * @param parent Node 父结点 */ Node(int id, Node parent) { this.id = id; if (parent != null) { this.parent = parent; parent.childs.add(this); this.level = parent.getLevel() + 1; } else { level = 0; } } /** * 得到结点ID. * @return int 结点ID */ public int getId() { return id; } /** * 得到结点在目录树中的层次. * 其中根结点为0,根结点的子结点为1,依次类推 * @return int 结点在目录树中的层次 */ final public int getLevel() { return level; } /** * 得到结点的值. * 也就是TreeNode接口所引用的对象 * @return Object 结点的值 */ final public Object getValue() { return value; } /** * 设定结点的值. */ final void setValue(Object value) { this.value = value; } /** * 得到子结点列表. * Iterator中存储的是Node对象 * @return Iterator 子结点列表 */ final public Iterator getChilds() { return childs.iterator(); } /** * 得到子结点数量. * @return int 子结点数量 */ final public int getChildsNumber() { return childs.size(); } /** * 是否有子结点. * @return boolean 有子结点返回true */ final public boolean hasChilds() { return childs.size() > 0; } /** * 得到父结点. * 如果结点为根结点,返回null * @return Node 父结点 */ final public Node getParent() { return parent; } /** * 得到第level级父结点. * @param level int 父结点的层次(level大于等于0,小于此结点的层次) * @return Node 第level级父结点 */ final public Node getParent(int level) { if (level < 0 || level >= this.level) { throw new ArrayIndexOutOfBoundsException("level is error."); } Node n = parent; for (int i = 1; i < level; i++) { n = n.getParent(); } return n; } /** * 得到结点在同级结点的相对位置. * @return int 结点在同级结点的相对位置 */ final public int getPosition() { if (parent == null) { return 0; } return parent.childs.indexOf(this); } /** * 结点是否是同级结点的最后一个. * @return boolean 是返回true */ final public boolean isLast() { if (parent == null) { return true; } return getPosition() == parent.childs.size() - 1; } /** * 结点是否同级结点的第一个. * @return boolean 是返回true */ final public boolean isFirst() { return getPosition() == 0; } /** * 得到目录树中下一个结点. * 如果此结点是目录树最后一个结点则返回null * @return Node 下一个结点 */ final public Node getNext() { if (childs.size() > 0) { return (Node) childs.get(0); } Node n = parent; while (n != null) { Node node = n.getNextSibling(); if (node != null) { return node; } n = n.getParent(); } return null; } /** * 得到下一个同级结点. * 没有下一个同级结点返回null * @return Node 下一个同级结点 */ final public Node getNextSibling() { if (parent == null) { return null; } int k = getPosition(); if (k == parent.getChildsNumber() - 1) { return null; } return (Node) parent.childs.get(k + 1); } /** * 得到前一个同级结点. * 没有前一个同级结点返回null * @return Node 前一个同级结点 */ final public Node getPreviousSibling() { int k = getPosition(); if (k == 0) { return null; } return (Node) parent.childs.get(k - 1); } /** * 得到前一个结点. * 根结点的前一个结点为null * @return Node 前一个结点 */ final public Node getPrevious() { Node n = getPreviousSibling(); if (n != null) { return n; } return parent; } }
/** * 结点过滤接口. <br> * 此接口用于从目录树中查找符合一定条件的结点。<br> * <p>Copyright: Copyright (c) 2004</p> * @author flyxxxxx * @version 1.0 */ public interface NodeFilter { /** * 判断结点是否满足一定条件. * @param n Node 要判断的结点 * @return boolean 满足条件返回true */ public boolean accept(Node n); } /** * 目录树的一个结点. <br> * 要将一组对象转化为目录树,它必须实现此接口或{@link Container}接口。<br> * 通过实现此接口的目录树,目录树的每个结点的标识等于实现此接口的相应对象的标识。<br> * 一个结点最重要的属性是它的标识和它上级目录的标识。<br> * <p>Copyright: Copyright (c) 2004</p> * @author flyxxxxx * @version 1.0 */ public interface TreeNode { /** * 得到此结点的标识 * @return String 结点的标识 */ public int getId(); /** * 得到父结点的标识 * @return String 父结点的标识 */ public int getParentId(); } /** * 容器接口. <br> * 要将一组或一个对象转化为目录树,它必须实现此接口或{@link TreeNode}接口。<br> * 容器能够包含子结点,它通过方法{@link #getChilds(java.lang.Object)}得到一个对象的子结点。<br> * 目录树中的每一个结点的值将引用这个容器所指向的对象,也就是说:通过调用方法 * {@link Node#getValue()}将得到这个对象。<br> * 如:Tree t=Tree.getTree(new File("D:/"),new FileContainer());<br> * 在t中的每个结点,对它调用方法{@link Node#getValue()}都将得到一个File对象。<br> * <p>Copyright: Copyright (c) 2004</p> * <p> </p> * @author flyxxxxx * @version 1.0 */ public interface Container { /** * 得到将对象的所有子对象. * @param obj Object 父对象 * @return Object[] 子对象列表 */ public Object[] getChilds(Object obj); }
import java.util.ArrayList; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.w3c.dom.Node; /** * XML文档容器. <br> * 将一个XML文件转化为目录树,此目录树中的每个结点将对应XML文件中的一个Element,也就是Document * 结点对应目录树的根结点,依次类推。<br> * 目录树的每个结点的值({@link Node#getValue()}均为Element,结点的ID由系统产生,根结点为0。<br> * 使用方法:Tree t=new Tree(Document document,new DocumentContainer());<br> * 其中document为XML文档元素。<br> * <p>Copyright: Copyright (c) 2004</p> * <p> </p> * @author flyxxxxx * @version 1.0 */ final public class DocumentContainer implements Container { /** * XML文档容器构造方法 */ public DocumentContainer() { } /** * 得到将对象的所有子对象. * @param obj Object 父对象(类型Element) * @return Object[] 子对象列表(类型Element[]) */ public Object[] getChilds(Object obj) { if (obj instanceof Element) { ArrayList rs = new ArrayList(); NodeList list = ( (Element) obj).getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node n = list.item(i); if (n.getNodeType() == Node.ELEMENT_NODE) { rs.add(n); } } return rs.toArray(); } throw new IllegalArgumentException( "Required param type is org.w3c.dom.Element."); } } import java.io.FileFilter; import java.io.File; /** * 文件容器. <br> * 将一个磁盘目录转化为目录树,此目录树中的每个结点将对应磁盘中的一个目录或文件。<br> * 目录树的每个结点的值({@link Node#getValue()}均为File,结点的ID由系统产生,根结点为0。<br> * 使用方法:Tree t=new Tree(File f,new FileContainer());<br> * <p>Copyright: Copyright (c) 2004</p> * <p> </p> * @author flyxxxxx * @version 1.0 */ final public class FileContainer implements Container { private FileFilter filter; /** * 默认容器构造方法 */ public FileContainer() { } /** * 带文件过滤器的构造方法. * 通过此方法,将目录树中不会有不满足条件的目录或文件 * @param filter FileFilter 文件过滤器 */ public FileContainer(FileFilter filter) { this.filter = filter; } /** * 得到将对象的所有子对象. * @param obj Object 父对象(类型File) * @return Object[] 子对象列表(类型File[]) */ public Object[] getChilds(Object obj) { if (obj instanceof File) { File f = (File) obj; if (f.isFile()) { return new Object[0]; } if (filter == null) { return f.listFiles(); } else { return f.listFiles(filter); } } throw new IllegalArgumentException("Required param type is java.io.File."); } }
JSP部分参考 <%@ page contentType="text/html; charset=GBK" %> <%@ page import="..."%>//未导入包 <%! static Hashtable images=new Hashtable(); static Hashtable actions=new Hashtable(); static String script; static { images.put("IMAGE_PLUS", "images/plus.gif"); images.put("IMAGE_PLUS_LAST", "images/pluslast.gif"); images.put("IMAGE_MINUS", "images/minus.gif"); images.put("IMAGE_MINUS_LAST", "images/minuslast.gif"); images.put("IMAGE_MIDBLK", "images/midblk.gif"); images.put("IMAGE_BLANK", "images/blank.gif"); images.put("IMAGE_LASTBLK", "images/lastblk.gif"); images.put("IMAGE_LINE", "images/line.gif"); images.put("IMAGE_FOLDER", "images/folder.gif"); images.put("IMAGES_FOLDER_OPEN","images/folderopen.gif"); StringBuffer sc=new StringBuffer("<script type=\"text/javascript\">\r\n"); Iterator imgs=images.values().iterator(); int k=0; while(imgs.hasNext()){ sc.append("var image"+k+"=new Image();\r\n"); sc.append("image"+k+".src=\""+(String)imgs.next()+"\";\r\n"); k++; } sc.append("</script>\r\n"); script=sc.toString(); actions.put("CLICK_FOLDER","clickFolder"); actions.put("CLICK_DOCUMENT","clickDocument"); actions.put("CLICK_FOLDER_IMG","openFolder"); actions.put("MOUSEOVER","overMouse"); actions.put("MOUSEOUT","outMouse"); } void paintChilds(Iterator childs,Writer w) throws IOException{ while(childs.hasNext()){ paintNode((Node)childs.next(),w); } } void paintNode(Node n,Writer w) throws IOException{ w.write("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\"><tr>"); int level=n.getLevel(); int id=n.getId(); Node parent=null; String name=((File)n.getValue()).getName(); boolean last=false; for(int i=level-1;i>0;i--){ parent=n.getParent(i); last=parent.isLast(); w.write("<td><img src=\"" + (String) images.get(last ? "IMAGE_BLANK":"IMAGE_LINE" ) + "\" border=\"0\"></td>"); } last=n.isLast(); if(n.hasChilds()){ w.write("<td id=\"plus" + id + "\" style=\"cursor:hand\" onClick=\""+ ((String)actions.get("CLICK_FOLDER_IMG")+"(document,"+id+")")+"\"><img src=\"" + (String) images.get(last ? "IMAGE_PLUS_LAST" : "IMAGE_PLUS") + "\" border=\"0\"></td>"); } else { w.write("<td id=\"plus" + id + "\"><img src=\"" + (String) images.get(last ? "IMAGE_MINUS_LAST" : "IMAGE_MINUS") + "\" border=\"0\"></td>"); } w.write("<td id=\"f" + id + "\"><img src=\"" + (String) images.get("IMAGE_FOLDER") + "\" border=\"0\"></td>"); if(n.hasChilds()){ w.write("<td id=\"td" + id + "\" style=\"cursor:hand\" onClick=\""+ (String)actions.get("CLICK_FOLDER")+"(document,"+id+"),"+ (String)actions.get("CLICK_FOLDER_IMG")+"(document,"+id+")"+"\">" + name + "</td>"); } else{ w.write("<td id=\"td" + id + "\" style=\"cursor:hand\" onClick=\""+ (String)actions.get("CLICK_FOLDER")+"(document,"+id+")"+"\">" + name + "</td>"); } w.write("</tr></table>"); if (n.hasChilds()) { w.write("<div id=\"div" + id + "\" style=\"display:none\">"); paintChilds(n.getChilds(), w); w.write("</div>"); } w.flush(); } %> <html> <head> <title> tree </title> <style type="text/css"> td{font:13px/16px;} A:link {text-decoration: none;font-size: 12px; color: #0000ff} A:visited {text-decoration: none;font-size: 12px; color: #0000ff} A:active {text-decoration: none;font-size: 12px} A:hover {text-decoration: underline;font-size: 12px} img{vertical-align: bottom;} </style> <%=script%> <script type="text/javascript"> function changeColor(doc,k){ var old=doc.thisForm.selectedNode.value; if(old!=k){ if(old!=""){ doc.all("td"+old).style.backgroundColor=doc.thisForm.bgColor.value; } doc.all("td"+k).style.backgroundColor=doc.thisForm.selectedColor.value; doc.thisForm.selectedNode.value=k; } } function clickDocument(doc,k){ changeColor(doc,k); alert("Click document "+doc.all("td"+k).innerText+"."); } function openFolder(doc,k){ var o=doc.all("div"+k); if(o.style.display=="none"){ o.style.display="block"; } else{ o.style.display="none"; } replaceImg(doc,k); var k=0; while((o=o.parentElement)!=doc&&o!=null){ k=o.id.indexOf("div"); if(k!=-1){ if(o.style.display=="none"){ o.style.display="block"; replaceImg(doc,o.id.substring(k)); } } } } function clickFolder(doc,k){ changeColor(doc,k); alert("Click folder "+doc.all("td"+k).innerText+"."); } function replaceImg(doc,k){ } </script> </head> <body> <% out.flush(); File f=new File(this.getServletConfig().getServletContext().getRealPath("/tree")).getParentFile(); Tree t=Tree.getTree(f,new FileContainer()); paintChilds(t.getChilds(),response.getWriter()); %> <form name="thisForm"> <input type="hidden" name="selectedNode"> <input type="hidden" name="bgColor" value="#FFFFFF"> <input type="hidden" name="selectedColor" value="#9999FF"> </form> </body> </html>
|