|
磁盘文件、XML文档和SQL中的表(有一定要求)都可以生成目录树。 此代码可以任意使用修改,如有修改、建议或发现错误,请发信到flyxxxxx@163.com,谢谢。
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator;
/** ?* 目录树.
?* 提供由其它对象生成目录树的支持。
?* 如磁盘目录:Tree t=Tree.getTree(new java.io.File("D:/"),new FileContainer());
?* XML文件:Document doc=...
?* Tree t=Tree.getTree(doc,new DocumentContainer());
?* 如果有File或Document以外的对象要生成目录树,请实现{@link TreeNode}或{@link Container}接口。 ?* 对于SQL中的表,建议字段要求id(标识)、parentId(上级目录标识),其它字段自定,然后对查询到的每一 ?* 条记录生成一个实现TreeNode接口的对象。 ?* 如果实现了TreeNode接口:
?*?? class Group implements TreeNode{}
?*?? Group[] groups=...
?*?? Tree t=Tree.getTree(groups,0);//其中0为根结点的ID ,并且groups不要求是同一对象,只要它们都实现了 ?* 接口TreeNode,并且ID不同,这样可以在生成目录树的过程中根据对象类型自行处理。 ?*
Copyright: Copyright (c) 2004
?*
?* @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},可以通过此方法加入目录树。
?? * 通过此方法创建的目录树,所有结点的ID由系统生成。obj将直接做为根结点,对根结点调用方法 ?? * {@link Node#getValue()}将得到obj。
?? * @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},可以通过此方法加入目录树。
?? * 通过此方法创建的目录树,所有结点的ID由系统生成,对根结点调用方法{@link Node#getValue()} ?? * 将得到null。
?? * 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不同,就可以将它们加入目录树。
?? * 通过此方法得到的目录树,它的根结点ID值为rootId,其它结点的值为实现接口TreeNode的对象的ID。
?? * 如果treeNode中包含了根结点,根结点的值可以通过方法{@link Node#getValue()}得到,返之得到的是null。
?? * 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;
/** ?* 目录树的一个结点.
?* 它的主要属性有结点标识、父结点、它在目录树中的层次(根结点为0)、结点的值、子结点。 ?*
Copyright: Copyright (c) 2004
?* @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; ? }
}
/** ?* 结点过滤接口.
* 此接口用于从目录树中查找符合一定条件的结点。
?*
Copyright: Copyright (c) 2004
?* @author flyxxxxx
?* @version 1.0
?*/
public interface NodeFilter
{
? /**
?? * 判断结点是否满足一定条件.
?? * @param n Node 要判断的结点
?? * @return boolean 满足条件返回true
?? */
? public boolean accept(Node n);
}
/** ?* 目录树的一个结点.
?* 要将一组对象转化为目录树,它必须实现此接口或{@link Container}接口。
?* 通过实现此接口的目录树,目录树的每个结点的标识等于实现此接口的相应对象的标识。
?* 一个结点最重要的属性是它的标识和它上级目录的标识。
?*
Copyright: Copyright (c) 2004
?* @author flyxxxxx
?* @version 1.0
?*/
public interface TreeNode {
? /** ?? * 得到此结点的标识 ?? * @return String 结点的标识 ?? */ ? public int getId();
? /** ?? * 得到父结点的标识 ?? * @return String 父结点的标识 ?? */ ? public int getParentId();
} /** ?* 容器接口.
?* 要将一组或一个对象转化为目录树,它必须实现此接口或{@link TreeNode}接口。
?* 容器能够包含子结点,它通过方法{@link #getChilds(java.lang.Object)}得到一个对象的子结点。
?* 目录树中的每一个结点的值将引用这个容器所指向的对象,也就是说:通过调用方法 ?* {@link Node#getValue()}将得到这个对象。
?* 如:Tree t=Tree.getTree(new File("D:/"),new FileContainer());
?* 在t中的每个结点,对它调用方法{@link Node#getValue()}都将得到一个File对象。
?*
Copyright: Copyright (c) 2004
?*
?* @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文档容器.
?* 将一个XML文件转化为目录树,此目录树中的每个结点将对应XML文件中的一个Element,也就是Document ?* 结点对应目录树的根结点,依次类推。
?* 目录树的每个结点的值({@link Node#getValue()}均为Element,结点的ID由系统产生,根结点为0。
?* 使用方法:Tree t=new Tree(Document document,new DocumentContainer());
?* 其中document为XML文档元素。
?*
Copyright: Copyright (c) 2004
?*
?* @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;
/** ?* 文件容器.
?* 将一个磁盘目录转化为目录树,此目录树中的每个结点将对应磁盘中的一个目录或文件。
?* 目录树的每个结点的值({@link Node#getValue()}均为File,结点的ID由系统产生,根结点为0。
?* 使用方法:Tree t=new Tree(File f,new FileContainer());
?*
Copyright: Copyright (c) 2004
?*
?* @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("
");??? 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("
????????????? (String) images.get(last ? "IMAGE_BLANK":"IMAGE_LINE" ) + ????????????? "/" border=/"0/"> | ");??? }??? last=n.isLast();??? if(n.hasChilds()){????? w.write("
??((String)actions.get("CLICK_FOLDER_IMG")+"(document,"+id+")")+"/"> ??????????????? (String) images.get(last ? "IMAGE_PLUS_LAST" : "IMAGE_PLUS") + ??????????????? "/" border=/"0/"> | ");??? }??? else {????? w.write("
????????????? (String) images.get(last ? "IMAGE_MINUS_LAST" : "IMAGE_MINUS") + ????????????? "/" border=/"0/"> | ");??? }??? w.write("
| ??????????? (String) images.get("IMAGE_FOLDER") + "/" border=/"0/"> | ");??? if(n.hasChilds()){????? w.write("
?(String)actions.get("CLICK_FOLDER")+"(document,"+id+"),"+ ?(String)actions.get("CLICK_FOLDER_IMG")+"(document,"+id+")"+"/">" + ?name + " | ");??? }??? else{????? w.write("
?(String)actions.get("CLICK_FOLDER")+"(document,"+id+")"+"/">" + ?? name + " | ");??? }??? w.write("
");
??? if (n.hasChilds()) {
????? w.write("
");
????? paintChilds(n.getChilds(), w);
????? w.write("
");
??? }
??? w.flush();
? }
%>
<BR>tree<BR>
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;}
<%=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>
<% out.flush(); File f=new File(this.getServletConfig().getServletContext().getRealPath("/tree")).getParentFile(); Tree t=Tree.getTree(f,new FileContainer()); paintChilds(t.getChilds(),response.getWriter()); %>
|