package edu.colostate.vchill.gui;
import edu.colostate.vchill.ControlMessage;
import edu.colostate.vchill.ViewControl;
import javax.swing.*;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.util.ArrayList;
import java.util.Enumeration;
/**
* Actions for ViewFileBrowser.
*
* @author Alexander Deyke
* @author Jochen Deyke
* @author jpont
* @version 2009-06-30
*/
public final class ViewFileBrowserActions {
private JTree tree;
/**
* Package protected constructor prevents instantiation
*
* @param tree the ViewFileBrowser's tree
*/
ViewFileBrowserActions(final JTree tree) {
this.tree = tree;
}
/**
* Tries to find the sweep specified by the
* provided control message.
*
* @param msg The message containing information
* for finding the sweep.
* @return A potentially modified control message
* indicating where the sweep is or null
* if the sweep couldn't be found.
*/
public ControlMessage findSweep(ControlMessage msg) {
System.out.println("File: " + msg.getFile());
String year = msg.getFile().substring(3, 7);
String month = msg.getFile().substring(7, 9);
String day = msg.getFile().substring(9, 11);
//I'll fix this later, it's so ugly it makes me want to cry.}
ViewControl vc = ViewControl.getInstance();
for (String url : vc.getConnections()) {
System.out.println("URL:" + url);
//determine the hidden part of the directories
FileTreeNode urlNode = this.getNode(url);
FileTreeNode dirNode = (FileTreeNode) urlNode.getFirstChild();
String fullPath = (String) dirNode.getUserObject();
String displayedPath = dirNode.toString();
int hiddenPathEnd = fullPath.indexOf(displayedPath);
if (hiddenPathEnd == -1) continue;
String hiddenPath = fullPath.substring(0, hiddenPathEnd);
/* First try looking for the complete path */
if (this.getNode(url, msg.getDir(), msg.getFile(), msg.getSweep()) != null) {
/* If found, set the URL for this connection and return */
System.out.println("Found complete path");
msg = msg.setURL(url);
return msg;
}
/* Complete path not found, so start looking for the pieces */
if (this.getNode(url, hiddenPath + year) == null)
continue;
if (this.getNode(url, hiddenPath + year + "/" + month) == null)
continue;
if (this.getNode(url, hiddenPath + year + "/" + month + "/" + day) == null)
continue;
if (this.getNode(url, hiddenPath + year + "/" + month + "/" + day, msg.getFile()) == null)
continue;
if (this.getNode(url, hiddenPath + year + "/" + month + "/" + day, msg.getFile(), msg.getSweep()) == null)
continue;
//we found the sweep
msg = msg.setURL(url);
msg = msg.setDir(hiddenPath + year + "/" + month + "/" + day);
return msg;
}
return null;
}
/**
* Changes the active selection in the tree to the path identified by the
* given control message. Each level of the tree is expanded as needed,
* allowing the expansion event handler to create the nodes at each level
* as needed. Assuming the desired node exists, it is then selected.
* Since this triggers a selection event, the sweep will be also be plotted.
*
* @param newSelection The desired selection.
*/
public void changeSelection(final ControlMessage newSelection) {
String pathToFind = newSelection.getDir().split(" ")[0];
String fileToFind = newSelection.getFile().split(" ")[0];
TreePath newPath = this.getTreePath(newSelection.getURL(), pathToFind, fileToFind, newSelection.getSweep());
if (newPath == null) return;
this.tree.scrollPathToVisible(newPath.getParentPath());
this.tree.scrollPathToVisible(newPath);
this.tree.clearSelection();
this.tree.setSelectionPath(newPath);
}
/**
* Finds and returns a node in the tree based on Strings. To select
* a file, leave the sweep null. To select a directory, leave
* the file null. To select a connection, leave the directory
* null. To select the root node, leave the connection null.
* Returns null if there's no match.
*
* @param url The connection URL.
* @param dir The directory.
* @param file The file.
* @param sweep The sweep.
* @return the requested node
*/
public FileTreeNode getNode(final String url, final String dir, final String file, final String sweep) {
TreePath path = this.getTreePath(url, dir, file, sweep);
if (path == null) return null;
else return (FileTreeNode) path.getLastPathComponent();
}
public TreePath getTreePath(final String url, final String dir, final String file, final String sweep) {
//System.out.println("looking for node");
FileTreeNode root = (FileTreeNode) tree.getModel().getRoot();
TreePath path = new TreePath(root);
this.tree.expandPath(path);
//System.out.println("looking for " + url);
if (url == null) return null;
FileTreeNode connection = searchNodeForString(root, url);
if (connection == null) return null;
path = path.pathByAddingChild(connection);
this.tree.expandPath(path);
//System.out.println("found connection " + connection);
//System.out.println("looking for " + dir + " in " + connection);
if (dir == null) return path;
FileTreeNode directory = searchNodeForString(connection, dir);
if (directory == null) return null;
path = path.pathByAddingChild(directory);
this.tree.expandPath(path);
//System.out.println("found dir " + directory);
while (true) {
directory = searchNodeForString(directory, dir);
if (directory == null) break;
path = path.pathByAddingChild(directory);
this.tree.expandPath(path);
//System.out.println("found dir " + directory);
}
directory = (FileTreeNode) path.getLastPathComponent();
//System.out.println("looking for " + file + " in " + directory);
if (file == null) return path;
FileTreeNode filenode = searchNodeForString(directory, file);
if (filenode == null) return null;
path = path.pathByAddingChild(filenode);
this.tree.expandPath(path);
//System.out.println("found file " + filenode);
//System.out.println("looking for " + sweep + " in " + filenode);
if (sweep == null) return path;
FileTreeNode sweepnode = searchNodeForString(filenode, sweep);
if (sweepnode == null) return null;
path = path.pathByAddingChild(sweepnode);
this.tree.expandPath(path);
//System.out.println("found sweep " + sweepnode);
return path;
}
/**
* Searches a node's children's userObjects for one matching the beginning of the specified String
*
* @param node the node to search
* @param toFind the String to search for
* @return the matching node; null if not found
*/
private static FileTreeNode searchNodeForString(final FileTreeNode node, final String toFind) {
Enumeration children = node.children();
while (children.hasMoreElements()) {
FileTreeNode child = (FileTreeNode) (children.nextElement());
String nodeName = ((String) child.getUserObject());
if (!nodeName.startsWith("Sweep")) nodeName = nodeName.split(" ")[0];
//System.out.println("nodeName = " + nodeName);
if (toFind.startsWith(nodeName)) return child;
if (nodeName.equals(toFind + "/")) return child;
}
return null;
}
/**
* Finds and returns a file node in the tree based on Strings.
*
* @param url The connection URL.
* @param dir The directory.
* @param file The file.
* @return the requested node
*/
public FileTreeNode getNode(final String url, final String dir, final String file) {
return this.getNode(url, dir, file, null);
}
/**
* Finds and returns a directory node in the tree based on Strings.
*
* @param url The connection URL.
* @param dir The directory.
* @return the requested node
*/
public FileTreeNode getNode(final String url, final String dir) {
return this.getNode(url, dir, null, null);
}
/**
* Finds and returns a connection node in the tree based on Strings.
*
* @param url The connection URL.
* @return the requested node
*/
public FileTreeNode getNode(final String url) {
return this.getNode(url, null, null, null);
}
/**
* Finds and returns the root node.
*
* @return the requested node
*/
public FileTreeNode getNode() {
return this.getNode(null, null, null, null);
}
/**
* Finds and returns a node based on a ControllMessage
*
* @param message ControlMessage corresponding to that node.
* @return the requested node
*/
public FileTreeNode getNode(final ControlMessage message) {
return this.getNode(message.getURL(), message.getDir(), message.getFile(), message.getSweep());
}
/**
* Selects the first sweep in the current file. If this is not possible
* (e.g. no previous selection), selects the first sweep in the tree. This
* method will not move the selection out of the current file.
*/
public void selectFirst() {
TreePath newSelection = null;
try {
Object[] nodes = this.tree.getSelectionPath().getPath();
TreeNode[] newNodes = new TreeNode[nodes.length];
for (int i = 0; i < nodes.length; ++i) newNodes[i] = (TreeNode) nodes[i];
newNodes[nodes.length - 1] = ((FileTreeNode) nodes[nodes.length - 2]).getFirstLeaf();
newSelection = new TreePath(newNodes);
} catch (Exception e) { //no previous selection - select first sweep in tree
ArrayList<FileTreeNode> path = new ArrayList<FileTreeNode>(5);
path.add(((FileTreeNode) this.tree.getModel().getRoot()));
tree.expandPath(new TreePath(path.toArray()));
FileTreeNode connection = (FileTreeNode) (((FileTreeNode) tree.getModel().getRoot()).getFirstChild());
path.add(connection);
tree.expandPath(new TreePath(path.toArray()));
FileTreeNode dir = (FileTreeNode) (connection.getFirstChild());
path.add(dir);
tree.expandPath(new TreePath(path.toArray()));
FileTreeNode file = (FileTreeNode) (dir.getFirstChild());
path.add(file);
tree.expandPath(new TreePath(path.toArray()));
FileTreeNode sweep = (FileTreeNode) (file.getFirstChild());
path.add(sweep);
newSelection = new TreePath(path.toArray());
}
this.tree.scrollPathToVisible(newSelection);
this.tree.setSelectionPath(newSelection);
}
/**
* Selects the previous sweep in the current file. If this is not possible
* (e.g. no previous selection), selects the first sweep in the tree. This
* method will not move the selection out of the current file.
*/
public void selectPrev() {
try {
TreePath newSelection = null;
Object[] nodes = tree.getSelectionPath().getPath();
int last = nodes.length - 1;
TreeNode[] newNodes = new TreeNode[nodes.length];
for (int i = 0; i < nodes.length; ++i) newNodes[i] = (TreeNode) nodes[i];
newNodes[last] = ((FileTreeNode) nodes[last]).getPreviousSibling();
if (newNodes[last] == null) return;
if (((FileTreeNode) newNodes[last]).getUserObject().equals(FileTreeNode.LOADING)) return;
newSelection = new TreePath(newNodes);
this.tree.scrollPathToVisible(newSelection);
this.tree.setSelectionPath(newSelection);
} catch (Exception e) { //no previous selection
selectFirst();
}
}
/**
* Selects the next sweep in the current file. If this is not possible
* (e.g. no previous selection), selects the first sweep in the tree. This
* method will not move the selection out of the current file.
*/
public void selectNext() {
try {
Object[] nodes = this.tree.getSelectionPath().getPath();
int last = nodes.length - 1;
TreeNode[] newNodes = new TreeNode[nodes.length];
for (int i = 0; i < nodes.length; ++i) newNodes[i] = (TreeNode) nodes[i];
newNodes[last] = ((FileTreeNode) nodes[last]).getNextSibling();
if (newNodes[last] == null) return;
if (((FileTreeNode) newNodes[last]).getUserObject().equals(FileTreeNode.LOADING)) {
System.out.println("Hit loading in selectNext");
return;
}
TreePath newSelection = new TreePath(newNodes);
this.tree.scrollPathToVisible(newSelection);
this.tree.setSelectionPath(newSelection);
} catch (Exception e) { //no previous selection
selectFirst();
}
}
/**
* Selects the next sweep in the current file, or, if the last sweep is
* already selected, the first sweep of the next file. If this is not
* possible (e.g. no previous selection), selects the first sweep in the
* tree. This method will not move the selection out of the current file.
*/
public void selectNextAcrossBounds() {
try {
Object[] nodes = this.tree.getSelectionPath().getPath();
int oldRow = tree.getLeadSelectionRow();
int last = nodes.length - 1;
TreeNode[] newNodes = new TreeNode[nodes.length];
for (int i = 0; i < last; ++i) newNodes[i] = (TreeNode) nodes[i];
newNodes[last] = ((FileTreeNode) nodes[last]).getNextSibling();
if (newNodes[last] == null) {
tree.expandRow(oldRow + 1);
tree.setSelectionRow(oldRow + 2);
return;
}
TreePath newSelection = new TreePath(newNodes);
this.tree.scrollPathToVisible(newSelection);
this.tree.setSelectionPath(newSelection);
} catch (Exception e) { //no previous selection
e.printStackTrace();
selectFirst();
}
}
/**
* Selects the last sweep in the current file. If this is not possible
* (e.g. no previous selection), selects the first sweep in the tree. This
* method will not move the selection out of the current file.
*/
public void selectLast() {
TreePath newSelection = null;
try {
Object[] nodes = this.tree.getSelectionPath().getPath();
TreeNode[] newNodes = new TreeNode[nodes.length];
for (int i = 0; i < nodes.length; ++i) newNodes[i] = (TreeNode) nodes[i];
newNodes[nodes.length - 1] = ((FileTreeNode) nodes[nodes.length - 2]).getLastLeaf();
newSelection = new TreePath(newNodes);
this.tree.scrollPathToVisible(newSelection);
this.tree.setSelectionPath(newSelection);
} catch (Exception e) { //no previous selection
selectFirst();
}
}
/**
* Completely rebuilds tree.
* Called on connect and on disconnect.
*/
public void refreshConnections() {
if (tree != null) {
FileTreeNode root = (FileTreeNode) this.tree.getModel().getRoot();
root.complete = false;
this.tree.collapseRow(0);
this.tree.expandRow(0);
/* Automatically expand connections. */
int numConnections = root.getChildCount();
for (int i = 0; i < numConnections; i++) {
FileTreeNode connection = (FileTreeNode) root.getChildAt(i);
if (connection.toString().equals("Local Filesystem"))
continue;
TreePath path = new TreePath(connection.getPath());
this.tree.expandPath(path);
}
}
}
public TreePath getPathForLocation(final int x, final int y) {
return this.tree.getPathForLocation(x, y);
}
public static ControlMessage getControlMessage(final TreePath path) {
if (path == null) return null;
Object[] pathObjects = path.getPath();
if (pathObjects.length < 4) return null;
return new ControlMessage(
(String) (((FileTreeNode) pathObjects[1]).getUserObject()), //host:port
pathObjects.length > 4 ? (String) (((FileTreeNode) pathObjects[pathObjects.length - 3]).getUserObject()) : "", //dir
(String) (((FileTreeNode) pathObjects[pathObjects.length - 2]).getUserObject()), //file
(String) (((FileTreeNode) pathObjects[pathObjects.length - 1]).getUserObject())); //sweep
}
}