package edu.washington.cs.oneswarm.watchdir;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import org.gudy.azureus2.core3.util.Constants;
import edu.uw.cse.netlab.reputation.GloballyAwareOneHopUnchoker;
public class UpdatingFileTree {
public static final long IDLE_THRESHOLD = 60 * 1000; // 1 minute without
// mods
private static Logger logger = Logger.getLogger(UpdatingFileTree.class.getName());
List<UpdatingFileTree> children = Collections
.synchronizedList(new ArrayList<UpdatingFileTree>());
File thisFile = null;
long lastRefreshed = 0;
boolean gone = false;
private UpdatingFileTreeListener mSpawn;
boolean mHasDirectoryChildren = false;
String mRelativePath = null;
public UpdatingFileTree(File root) {
this(root, new UpdatingFileTreeListener() {
public void broadcastChange(UpdatingFileTree path, boolean isDelete) {
}
}, Integer.MAX_VALUE);
}
public UpdatingFileTree(File root, UpdatingFileTreeListener spawn) {
this(root, spawn, Integer.MAX_VALUE);
}
public UpdatingFileTree(File root, UpdatingFileTreeListener spawn, int maxDepth) {
thisFile = root;
mSpawn = spawn;
lastRefreshed = thisFile.lastModified();
if (thisFile.isDirectory()) {
File[] files = thisFile.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
/**
* We don't want to hash trash on windows
*/
if (name.toLowerCase().equals("recycler") && Constants.isWindows)
return false;
return name.startsWith(".") == false; // skip hidden files
}
});
if (files != null && maxDepth > 0) {
List<UpdatingFileTree> children = new ArrayList<UpdatingFileTree>(files.length);
for (int i = 0; i < files.length; i++) {
UpdatingFileTree c = new UpdatingFileTree(files[i], mSpawn);
if (c != null) // we might not have perms everywhere...
{
if (c.thisFile.lastModified() + IDLE_THRESHOLD < System.currentTimeMillis()) {
children.add(c);
if (c.isDirectory()) {
mHasDirectoryChildren = true;
}
} else {
logger.fine("skipping fresh child: " + c.thisFile.getAbsolutePath());
}
}
}
this.children = children;
} else {
gone = true;
}
}
}
public long getLastModified() {
return lastRefreshed;
}
/**
* so we get directories before their files...
*/
public void broadcast() {
mSpawn.broadcastChange(this, false);
for (UpdatingFileTree c : children)
c.broadcast();
}
public boolean isDirectory() {
return thisFile.isDirectory();
}
public void update() {
logger.finest("update of: " + this.getThisFile().getAbsolutePath());
if (thisFile.lastModified() != lastRefreshed || mHasDirectoryChildren) {
if (thisFile.exists() == false) {
this.gone = true;
}
/**
* 1. check if children still there
*/
mHasDirectoryChildren = false;
if (children != null) {
for (UpdatingFileTree c : children.toArray(new UpdatingFileTree[0])) {
if (c.gone) {
children.remove(c);
logger.finest("child gone this pass: " + c.getThisFile().getAbsolutePath());
} else {
c.update();
if (c.isDirectory()) {
mHasDirectoryChildren = true;
}
}
}
}
/**
* 2. Check if there are new children
*/
File[] files = thisFile.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith(".") == false; // skip hidden files
}
});
if (files != null) {
for (int i = 0; i < files.length; i++) {
boolean newFile = true;
for (UpdatingFileTree c : children) {
if (c.thisFile.equals(files[i]) == true) {
newFile = false;
break;
}
}
if (newFile) {
UpdatingFileTree neu = new UpdatingFileTree(files[i], mSpawn);
long thresh = neu.thisFile.lastModified() + IDLE_THRESHOLD;
if (thresh < System.currentTimeMillis()) {
children.add(neu);
if (neu.isDirectory()) {
mHasDirectoryChildren = true;
}
neu.broadcast();
} else {
logger.fine("skipping too-fresh file: "
+ neu.thisFile.getAbsolutePath() + " "
+ (thresh - System.currentTimeMillis()) + "s left");
}
}
}
} else if (thisFile.exists() == false) {
logger.finest(getThisFile().getAbsolutePath() + " will be gone next pass");
gone = true; // will get removed during the next pass
mSpawn.broadcastChange(this, true);
}
} else if (thisFile.exists() == false) {
logger.finest(getThisFile().getAbsolutePath() + " will be gone next pass");
gone = true; // will get removed during the next pass
mSpawn.broadcastChange(this, true);
}
}
public List<UpdatingFileTree> getDirectoryChildren() {
if (mHasDirectoryChildren == false) {
return new ArrayList<UpdatingFileTree>(0);
} else {
List<UpdatingFileTree> out = new ArrayList<UpdatingFileTree>();
for (UpdatingFileTree u : children) {
if (u.isDirectory() && !u.gone)
out.add(u);
}
return out;
}
}
public List<UpdatingFileTree> getFileChildren() {
List<UpdatingFileTree> out = new ArrayList<UpdatingFileTree>();
for (UpdatingFileTree u : children) {
if (u.isDirectory() == false && !u.gone)
out.add(u);
}
return out;
}
public List<UpdatingFileTree> getChildren() {
return children;
}
public String toString() {
return thisFile.getName()
+ (isDirectory() && children != null ? " (" + children.size() + " children)" : "");
}
public File getThisFile() {
return thisFile;
}
public long modifiedChecksum() {
long sum = getLastModified();
for (UpdatingFileTree k : children) {
sum += k.modifiedChecksum();
}
return sum;
}
}