package org.greenstone.gatherer.file; import java.io.File; import java.util.*; import java.util.regex.*; import javax.swing.event.*; import javax.swing.tree.*; import org.greenstone.gatherer.DebugStream; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.gui.tree.DragTree; public class FileSystemModel extends DefaultTreeModel implements TreeExpansionListener, TreeWillExpandListener { private DragTree tree = null; private FileFilter current_filter = null; private FileFilter[] filters = null; /** The filters in place for any file system model. */ private FileFilter[] default_filters = { new FileFilter("\\..*", true), new FileFilter("metadata\\.xml", true) }; public FileSystemModel(FileNode root) { super(root); root.setModel(this); root.refresh(); } public int getChildCount(Object parent) { return ((TreeNode)parent).getChildCount(); } public FileFilter[] getFilters() { if(filters == null) { if(current_filter != null) { filters = new FileFilter[default_filters.length + 1]; filters[default_filters.length] = current_filter; } else { filters = new FileFilter[default_filters.length]; } System.arraycopy(default_filters, 0, filters, 0, default_filters.length); } return filters; } /** Retrieve the node denoted by the given tree path. Note that this isn't equivelent to saying path.lastPathComponent, as the references within the path may be stale. */ public FileNode getNode(TreePath path) { FileNode last = (FileNode)path.getLastPathComponent(); DebugStream.println("Last Path Component = " + last); return last; /* ///atherer.println("**** getNode(" + path + ") ****"); FileNode current = (FileNode)root; // Special case for the root node. Check the first path component is the root node. FileNode first_node = (FileNode)path.getPathComponent(0); if(current.equals(first_node)) { DebugStream.println("First path component matches root node."); // For each path with this tree path for(int i = 1; current != null && i < path.getPathCount(); i++) { // Retrieve the stale path Object stale_object = path.getPathComponent(i); FileNode stale_node = null; if(stale_object instanceof FileNode) { stale_node = (FileNode) stale_object; } DebugStream.print("Searching for '" + stale_object + "': "); // Locate the fresh node by searching current's children. Remember to ensure that current is mapped. boolean found = false; // First we search through the mapped children for(int j = 0; !found && j < current.getChildCount(); j++) { FileNode child_node = (FileNode) current.getChildAt(j); DebugStream.print(child_node + " "); if((stale_node != null && stale_node.equals(child_node)) || stale_object.toString().equals(child_node.toString())) { found = true; current = child_node; DebugStream.println("Found!"); } child_node = null; } // Failing that we search through all the children, including filtered files for(int j = 0; !found && j < current.size(); j++) { FileNode child_node = (FileNode) current.getChildAtUnfiltered(j); DebugStream.print(child_node + " "); if((stale_node != null && stale_node.equals(child_node)) || stale_object.toString().equals(child_node.toString())) { found = true; current = child_node; DebugStream.println("Found!"); } child_node = null; } // If no match is found, then set current to null and exit. if(!found) { current = null; DebugStream.println("Not Found!"); } else { DebugStream.println("Returning node: " + new TreePath(current.getPath())); } // Repeat as necessary } } return current; */ } public void insertNodeInto(MutableTreeNode newChild, MutableTreeNode parent, int index) { ///ystem.err.println("insertNodeInto(" + newChild + ", " + parent + ", " + index + ")"); super.insertNodeInto(newChild, parent, index); } public void refresh(TreePath path) { // Can only refresh if the model is currently being displayed in a tree if (tree == null) { return; } // If no path is set, take the path to the root node (ie. update the whole tree) if (path == null) { // System.err.println("\nFileSystemModel.refresh(entire tree)."); path = new TreePath(((FileNode) root).getPath()); // Make sure the root node is expanded tree.expandPath(path); } // else { // System.err.println("\nFileSystemModel.refresh(" + path + ")."); // } // Record all the expanded paths under this node Enumeration old_expanded_paths_enumeration = tree.getExpandedDescendants(path); if (old_expanded_paths_enumeration == null) { return; } // Map and unmap the node to refresh its contents FileNode node = (FileNode) path.getLastPathComponent(); node.refresh(); // Fire the appropriate event nodeStructureChanged(node); // Sort the old expanded paths by length, smallest first ArrayList old_expanded_paths_list = Collections.list(old_expanded_paths_enumeration); Collections.sort(old_expanded_paths_list, new TreePathComparator()); // Restore each of the expanded paths for (int i = 0; i < old_expanded_paths_list.size(); i++) { TreePath old_expanded_path = (TreePath) old_expanded_paths_list.get(i); // System.err.println("Expanded path: " + old_expanded_path); // Build up the new path in the tree TreePath current_path = new TreePath(path.getPath()); FileNode current_node = node; // Traverse the tree to find the node to expand (or find it no longer exists) while (!current_path.toString().equals(old_expanded_path.toString())) { // System.err.println("Current path: " + current_path); FileNode old_expanded_node = (FileNode) old_expanded_path.getPathComponent(current_path.getPathCount()); // System.err.println("Looking for: " + old_expanded_node); // Find the child node that matches the next element in the path boolean found = false; for (int j = 0; j < current_node.getChildCount(); j++) { FileNode child_node = (FileNode) current_node.getChildAt(j); // System.err.println("Child node: " + child_node); if (child_node.equals(old_expanded_node)) { // System.err.println("Found!"); current_path = current_path.pathByAddingChild(child_node); current_node = child_node; found = true; break; } } // The node was not found, so we cannot expand this path if (!found) { // System.err.println("Not found..."); break; } } // If we have built up the correct path, expand it if (current_path.toString().equals(old_expanded_path.toString())) { tree.expandPath(current_path); } } } // The file tree can be filtered, so check that the node to be removed is actually part of the model public void removeNodeFromParent(MutableTreeNode node) { TreeNode parent_node = ((FileNode) node).getParent(); if (parent_node != null && parent_node.getIndex((FileNode) node) != -1) { super.removeNodeFromParent(node); } } private class TreePathComparator implements Comparator { public int compare(Object o1, Object o2) { return (((TreePath) o1).getPathCount() - ((TreePath) o2).getPathCount()); } } public void setFilter(String pattern) { if(pattern != null) { current_filter = new FileFilter(pattern, false); } else { current_filter = null; } filters = null; } public void setTree(DragTree tree) { this.tree = tree; } public String toString() { if(tree != null) { return tree.toString(); } return "FileSystemModel"; } /** Called whenever an item in the tree has been collapsed. */ public void treeCollapsed(TreeExpansionEvent event) { // Deallocate the affected nodes children. Don't need to do this in a swing worker, as the nodes children are currently not visable. TreePath path = event.getPath(); FileNode node = (FileNode) path.getLastPathComponent(); node.unmap(); // Fire the appropriate event. nodeStructureChanged(node); } /** Called whenever an item in the tree has been expanded. */ public void treeExpanded(TreeExpansionEvent event) { } /** Invoked whenever a node in the tree is about to be collapsed. */ public void treeWillCollapse(TreeExpansionEvent event) throws ExpandVetoException { // Veto the event if the user is attempting to collapse the root node (regardless of whether it is visible). TreePath path = event.getPath(); if(path.getPathCount() == 1) { throw new ExpandVetoException(event, "Cannot collapse root node!"); } } /** Invoked whenever a node in the tree is about to be expanded. */ public void treeWillExpand(TreeExpansionEvent event) throws ExpandVetoException { // Set the wait cursor Gatherer.g_man.wait(true); // Allocate the children (don't need a swing worker since the node's children are currently not visible) TreePath path = event.getPath(); FileNode node = (FileNode) path.getLastPathComponent(); node.refresh(); nodeStructureChanged(node); // Restore the cursor Gatherer.g_man.wait(false); } }