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.Dictionary; import org.greenstone.gatherer.Gatherer; import org.greenstone.gatherer.file.FileFilter; import org.greenstone.gatherer.file.FileNode; import org.greenstone.gatherer.file.FileOpenActionListener; import org.greenstone.gatherer.gui.tree.DragTree; import org.greenstone.gatherer.util.SynchronizedTreeModelTools; public class FileSystemModel extends DefaultTreeModel implements TreeExpansionListener, TreeWillExpandListener { private int counter = 0; private DragTree tree; private FileFilter current_filter; private FileFilter[] filters; /** 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); current_filter = null; filters = null; tree = null; root.setModel(this); root.map(); } 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) { ///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)) { Gatherer.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; } Gatherer.print("Searching for '" + stale_object + "': "); // Locate the fresh node by searching current's children. Remember to ensure that current is mapped. //current.unmap(); 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); Gatherer.print(child_node + " "); if((stale_node != null && stale_node.equals(child_node)) || stale_object.toString().equals(child_node.toString())) { found = true; current = child_node; Gatherer.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.get(j); Gatherer.print(child_node + " "); if((stale_node != null && stale_node.equals(child_node)) || stale_object.toString().equals(child_node.toString())) { found = true; current = child_node; Gatherer.println("Found!"); } child_node = null; } // If no match is found, then set current to null and exit. if(!found) { current = null; Gatherer.println("Not Found!"); } else { Gatherer.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 mapDirectory(File directory, String title) { FileNode node = new FileNode(directory, this, title, true); SynchronizedTreeModelTools.insertNodeInto(this, (FileNode)root, node); } 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); } } } 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(); ///ystem.err.println("Unmap: " + node); 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 to do this in a swing worker, as the nodes children are currently not visable. TreePath path = event.getPath(); FileNode node = (FileNode) path.getLastPathComponent(); ///ystem.err.println("Mapping: " + node); node.map(); ///ystem.err.println(" -> node has " + node.getChildCount() + " children"); nodeStructureChanged(node); // Restore the cursor. Gatherer.g_man.wait(false); } }