看很多人要求目录树,我的代码可以满足大多数要求 作  者: flyxxxxx (灭神)

磁盘文件、XML文档和SQL中的表(有一定要求)都可以生成目录树。
此代码可以任意使用修改,如有修改、建议或发现错误,请发信到flyxxxxx@163.com,谢谢。

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>
致歉:
  刚刚帮一网友调试的过程中发现一Bug。
  这种情况只在方法参数TreeNode[] treeNode中没有根结点时发生(包含了根结点的不会发生错误)。
  类Tree中if ( (TreeNode) last.getValue()).getId() == tnode.getParentId())
  这一行应该改为if ( last.getValue()!=null&&( (TreeNode) last.getValue()).getId() == tnode.getParentId())
  在此向大家表示歉意,2004-07-21 08:41:00 以前发过的,我会将更正过的发给大家。之后发的将是正确的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值