/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*/
package org.sintef.thingml;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
/**
* A simple file manager, that can turn a directory tree into a tree of nodes,
* suitable to be used by a <code>JTree</code>.
*/
public class SimpleFileManager {
/**
* How deep to descend into a directory tree at most. This is a safety
* feature, guarding against loops created by symbolic links as well as
* a performance tweak.
*/
public static final int MAXDEPTH = 10;
/**
* The root directory to which all pathes are relative
*/
private File root;
/**
* Cache the directory tree
*/
private DefaultMutableTreeNode dirCache;
/**
* A thread, that monitors the directory tree for changes.
*/
private Thread monitorThread;
/**
* The <code>FileMonitor</code> of the
* <code>monitorThread</code>
*/
private FileMonitor fileMonitor;
/**
* The <code>FileFilter</code>, stating which files will be included in the
* tree.
*/
private FileFilter filter;
/**
* Construct a new file manager
* @param root The toplevel directory of the directory tree to manage
* @param filter a <code>FileFilter</code> for filtering the directory tree.
* @exception IOException if root is inaccessible.
*/
public SimpleFileManager(File root, FileFilter filter) throws IOException {
if (root == null) throw new NullPointerException();
this.filter = filter;
if (!root.exists() || !root.canRead()) {
throw new IOException(root.getAbsolutePath());
}
this.root = root;
}
/**
* Query the root of this manager
*/
public File getRoot() {
return root;
}
/**
* Construct a tree of nodes, that represents the currently
* being monitored directory tree and can directly be used by
* a <code>JTree</code>.
* @return the top node of the tree or null if construction failed
* for whatever reason.
*/
public synchronized DefaultMutableTreeNode getDirectoryTree() {
if (dirCache == null) {
dirCache = descend(root, MAXDEPTH);
}
return dirCache;
}
/**
* Causes the filemanager to throw away all internal caches and rescan
* it's directory tree.
*/
public synchronized void refresh() {
dirCache = null;
}
/**
* Helper function to recursively travel through the directory tree
* @param dir the directory to descend into
* @param depth depth counter. Will bail out, when this reaches 0.
*/
private DefaultMutableTreeNode descend(File dir, int depth) {
DefaultMutableTreeNode ret = new DefaultMutableTreeNode(dir.getName());
File[] lst = dir.listFiles();
try {
Arrays.sort(lst);
} catch (Exception e) {
}
for (int i = 0; i < lst.length; i++) {
if (depth > 0 && lst[i].canRead()) {
if (lst[i].isDirectory()) {
DefaultMutableTreeNode tmp = descend(lst[i], depth - 1);
if (tmp != null) {
ret.add(tmp);
}
} else {
if (filter.accept(lst[i])) {
ret.add(new DefaultMutableTreeNode(lst[i].getName()));
}
}
}
}
if (ret.getChildCount() == 0) {
ret = null;
}
return ret;
}
/**
* Stop monitoring the directory tree.
*/
public void stopMonitoring() {
try {
monitorThread.interrupt();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Start directory monitoring
*/
public void startMonitoring() {
fileMonitor = new FileMonitor(root, this);
monitorThread = new Thread(fileMonitor);
monitorThread.start();
}
/**
* Query the monitor thread of this file manager
* @return the thread, that is responsible for keeping
* the filemanager in sync with the directory tree. Or null
* if we are not monitoring currently.
*/
public FileMonitor getFileMonitor() {
return fileMonitor;
}
/**
* Get the URI for a node
* @param node a node created by this manager
* @return the URI of the file, corresponding to the node, relative to
* the root directory, this manager manages.
*/
public URI getRelativePath(DefaultMutableTreeNode node) {
File f = root.getParentFile();
TreeNode[] tmp = node.getPath();
for (int i = 0; i < tmp.length; i++) {
f = new File(f, tmp[i].toString());
}
URI base = root.toURI();
return base.relativize(f.toURI());
}
/**
* Find a file
* @param u a relative URI (relative to this managers root directory).
* @return the corresponsing file.
*/
public File getFile(URI u) {
File f = new File(root.toURI().resolve(u));
// NOTE: URI.resolve() kills things like "../../..", so we should be safe
// from a malicious scene hopping around in the file system.
return f;
}
/**
* Break a relative URI into components
* @param u An URI as produced by <code>getRelativePath()</code>
* @return An array of path components, including the root directory
* as the first one.
*/
public String[] getComponents(URI u) {
if (u == null) return new String[0];
String tmp[] = u.getPath().split("/");
String ret[] = new String[tmp.length + 1];
ret[0] = root.getName();
System.arraycopy(tmp, 0, ret, 1, tmp.length);
return ret;
}
}