/* * (C) 2004 - Geotechnical Software Services * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ package no.geosoft.cc.directory; import java.util.*; import java.io.File; import java.text.Collator; import javax.swing.tree.TreeModel; import javax.swing.tree.TreePath; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeModelEvent; import javax.swing.*; // For testing only /** * A TreeModel implementation for a disk directory structure. * * Typical usage: * * <pre> * FileSystemModel model = new FileSystemModel (new File ("/")); * JTree tree = new JTree (model); * </pre> * * @author <a href="mailto:info@geosoft.no">GeoSoft</a> */ public class FileSystemModel implements TreeModel { private Collection listeners_; private FileTreeNode root_; private HashMap sortedChildren_; // File -> List<File> private HashMap lastModified_; /** * Create a tree model using the specified file as root. * * @param root Root file (directory typically). */ public FileSystemModel (File root) { root_ = new FileTreeNode(root); listeners_ = new ArrayList(); sortedChildren_ = new HashMap(); lastModified_ = new HashMap(); } public Object getRoot() { return root_; } public Object getChild (Object parent, int index) { List children = (List) sortedChildren_.get (parent); return children == null ? null : children.get (index); } public int getChildCount (Object parent) { File file = (File) parent; if (!file.isDirectory()) return 0; File[] children = file.listFiles(); int nChildren = children == null ? 0 : children.length; long lastModified = file.lastModified(); boolean isFirstTime = lastModified_.get (file) == null; boolean isChanged = false; if (!isFirstTime) { Long modified = (Long) lastModified_.get (file); long diff = Math.abs (modified.longValue() - lastModified); isChanged = diff > 4000; // MS/Win or Samba HACK. Check this! } // Sort and register children info if (isFirstTime || isChanged) { lastModified_.put (file, new Long (lastModified)); TreeSet sorted = new TreeSet(); for (int i = 0; i < nChildren; i++) sorted.add (new FileTreeNode (children[i])); sortedChildren_.put (file, new ArrayList (sorted)); } // Notify listeners (visual tree typically) if changes if (isChanged) { TreeModelEvent event = new TreeModelEvent (this, getTreePath (file)); fireTreeStructureChanged (event); } return nChildren; } private Object[] getTreePath (File file) { List path = new ArrayList(); while (!file.equals(root_)) { path.add(file); file = file.getParentFile(); } path.add(root_); int nElements = path.size(); Object[] treePath = new Object[nElements]; for (int i = 0; i < nElements; i++) treePath[i] = path.get(nElements - i - 1); return treePath; } public boolean isLeaf (Object node) { return ((File) node).isFile(); } public void valueForPathChanged (TreePath path, Object newValue) { } public int getIndexOfChild (Object parent, Object child) { List children = (List) sortedChildren_.get(parent); return children.indexOf (child); } public void addTreeModelListener (TreeModelListener listener) { if (listener != null && !listeners_.contains(listener)) listeners_.add (listener); } public void removeTreeModelListener (TreeModelListener listener) { if (listener != null) listeners_.remove (listener); } public void fireTreeNodesChanged (TreeModelEvent event) { for (Iterator i = listeners_.iterator(); i.hasNext();) { TreeModelListener listener = (TreeModelListener) i.next(); listener.treeNodesChanged (event); } } public void fireTreeNodesInserted (TreeModelEvent event) { for (Iterator i = listeners_.iterator(); i.hasNext();) { TreeModelListener listener = (TreeModelListener) i.next(); listener.treeNodesInserted (event); } } public void fireTreeNodesRemoved (TreeModelEvent event) { for (Iterator i = listeners_.iterator(); i.hasNext();) { TreeModelListener listener = (TreeModelListener) i.next(); listener.treeNodesRemoved (event); } } public void fireTreeStructureChanged (TreeModelEvent event) { for (Iterator i = listeners_.iterator(); i.hasNext();) { TreeModelListener listener = (TreeModelListener) i.next(); listener.treeStructureChanged (event); } } /** * Extension to the java.io.File object but with more * appropriate compare rules * * @author <a href="mailto:info@geosoft.no">GeoSoft</a> */ private class FileTreeNode extends File implements Comparable { public FileTreeNode(File file) { super(file, ""); } /** * Compare two FileTreeNode objects so that directories * are sorted first. * * @param object Object to compare to. * @return Compare identifier. */ public int compareTo (Object object) { File file1 = this; File file2 = (File) object; Collator collator = Collator.getInstance(); if (file1.isDirectory() && file2.isFile()) return -1; else if (file1.isFile() && file2.isDirectory()) return +1; else return collator.compare(file1.getName(), file2.getName()); } /** * Retur a string representation of this node. * The inherited toString() method returns the entire path. * For use in a tree structure, the name is more appropriate. * * @return String representation of this node. */ public String toString() { return getName(); } } /** * Testing this class. * * @param args Not used */ public static void main (String args[]) { JFrame f = new JFrame(); f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); // The entire Linux root partition FileSystemModel model = new FileSystemModel (new File ("/")); JTree tree = new JTree (model); f.getContentPane().add (new JScrollPane (tree)); f.pack(); f.setVisible (true); } }