import java.awt.Component;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.Timer;

import javax.swing.*;
import javax.swing.tree.*;

public class FileReloadedTreeModel extends DefaultTreeModel {

  public FileReloadedTreeModel() {
    super(null);
  }

  public class MyTreeNode implements TreeNode, Comparable {

    public MyTreeNode(MyTreeNode parent,File file) {
      this.parent=parent;
      this.file=file;
    }
    
    public boolean equals(Object o) {
      if (!(o instanceof MyTreeNode))
        return false;
        
      return file.getName().equals(((MyTreeNode)o).file.getName());
    }
    
    public int compareTo(Object o) {
      return file.getName().compareTo(((MyTreeNode)o).file.getName());
    }
  
    public int getChildCount() {
      return getChildren().length;
    }

    public boolean getAllowsChildren() {
     return true;
    }

    public boolean isLeaf() {
      return !file.isDirectory();
    }

    public Enumeration children() {
     return Collections.enumeration(Arrays.asList(getChildren()));
    }

    public TreeNode getParent() {
      return parent;
    }
  
    public File getFile() {
      return file;
    }

    public TreeNode getChildAt(int childIndex) {
      return getChildren()[childIndex];
    }

    public int getIndex(TreeNode node) {
      TreeNode[] children=getChildren();
      for(int i=0;i<children.length;i++)
        if (children[i].equals(node))
          return i;
        
      return -1;
    }
  
    private TreeNode[] getChildren() {
      if (children!=null)
        return children;
      
      return reload();
    }
  
    public MyTreeNode[] reload() {
      MyTreeNode[] oldChildren=this.children;
      MyTreeNode[] children=processChildren();
      
      if (oldChildren==null) {
        schedule(this);
        this.children=children;
        return children;
      }
      
      ArrayList removed=new ArrayList();
      ArrayList added=new ArrayList();
      
      int i=0,j=0,k=0;
      for(;i<oldChildren.length && j<children.length;) {
        MyTreeNode oldNode=oldChildren[i];
        MyTreeNode node=children[j];
        int comp=oldNode.compareTo(node);
        if (comp<0) {
          removed.add(new Integer(i++));
        }
        else
          if (comp>0) {
            added.add(new Integer(k++));
          } 
          else {
            i++;
            j++;
            k++;
          }
      }
      
      for(;i<oldChildren.length;i++)
        removed.add(new Integer(i));
      
      for(;j<children.length;j++)
        added.add(new Integer(j));
        
      System.out.println("children "+Arrays.asList(children));
      System.out.println("added "+added);
       System.out.println("children "+Arrays.asList(oldChildren));
      System.out.println("removed "+removed);
      
      Object[] path=getPathToRoot(this);
      
      schedule(this);
      
      int[] removedIndicies=getIndicies(removed);
      fireTreeNodesRemoved(this, path, removedIndicies, getChangedNodes(removedIndicies,oldChildren));
      
      int[] addedIndicies=getIndicies(added);
      
      this.children=children;
      fireTreeNodesInserted(this, path, addedIndicies, getChangedNodes(addedIndicies,children));
      
      return children;
    }
    
    
    
    private MyTreeNode[] processChildren() {
      File[] files=file.listFiles();
      if (files==null)
        return new MyTreeNode[0];
      
      MyTreeNode[] children=new MyTreeNode[files.length];
      for(int i=0;i<children.length;i++)
        children[i]=new MyTreeNode(this,files[i]);
        
      Arrays.sort(children);

      return children;
    }
    
    public String toString() {
      return file.getName();
    }
  
    private final MyTreeNode parent;
    final File file;
    private MyTreeNode[] children;
  }
  
  static int[] getIndicies(ArrayList list) {
    int[] indicies=new int[list.size()];
    for(int i=0;i<list.size();i++) {
      indicies[i]=((Integer)list.get(i)).intValue();
    }
      
    return indicies; 
  }
    
  static MyTreeNode[] getChangedNodes(int[] list, MyTreeNode[] nodes) {
    MyTreeNode[] changedNodes=new MyTreeNode[list.length];
    for(int i=0;i<list.length;i++)
      changedNodes[i]=nodes[list[i]];
    return changedNodes; 
  }
  
  public void schedule(final MyTreeNode node) {
    
    System.err.println("schedule "+node);
    
    timer.schedule(new TimerTask() {
        public void run() {
          try {
            SwingUtilities.invokeAndWait(new Runnable() {
              public void run() {
                node.reload();
                nodeChanged(node);
              }
            });
          } catch (InterruptedException e) {
            e.printStackTrace();
          } catch (InvocationTargetException e) {
            Throwable cause=e.getCause();
            if (cause instanceof RuntimeException)
              throw (RuntimeException)cause;
            else
              if (cause instanceof Error)
                throw (Error)cause;
              else
                throw new Error(cause);
          }
        }
      }, 5000);
  }
  
  private final Timer timer=new Timer(true);
  
  public static void main(String[] args) {
    
    File dir;
    if (args.length>0)
      dir=new File(args[0]);
    else
      dir=new File(".");
    
    FileReloadedTreeModel model=new FileReloadedTreeModel();
    model.setRoot(model.new MyTreeNode(null,dir));
    
    JTree tree=new JTree(model);
    tree.setCellRenderer(new DefaultTreeCellRenderer() {
      public Component getTreeCellRendererComponent(JTree tree,
        Object value,boolean selected,boolean expanded,boolean leaf,int row,
        boolean hasFocus) {
         
        MyTreeNode node=(MyTreeNode)value;
          
        return super.getTreeCellRendererComponent(
          tree,node.getFile().getName(),
          selected,expanded,leaf,row,hasFocus);
      }

    });
    
    JScrollPane pane=new JScrollPane(tree);
    
    final JFrame frame=new JFrame();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setContentPane(pane);
    frame.setSize(400,300);
    frame.show();
  }
}