package org.smartly.commons.io.repository;
import java.io.File;
import java.io.IOException;
import java.lang.ref.SoftReference;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* Repository implementation for directories providing file resources
*/
public class FileRepository extends AbstractRepository {
// Directory serving sub-repositories and file resources
protected final File directory;
protected long lastModified = -1;
protected long lastChecksum = 0;
protected long lastChecksumTime = 0;
/**
* Defines how long the checksum of the repository will be cached
*/
final long cacheTime = 1000L;
/**
* Constructs a FileRepository using the given argument
*
* @param path absolute path to the directory
* @throws java.io.IOException if canonical path couldn't be resolved
*/
public FileRepository(String path) throws IOException {
this(new File(path), null);
}
/**
* Constructs a FileRepository using the given directory as top-level
* repository
*
* @param dir directory
* @throws java.io.IOException if canonical path couldn't be resolved
*/
public FileRepository(File dir) throws IOException {
this(dir, null);
}
/**
* Constructs a FileRepository using the given directory and top-level
* repository
*
* @param dir directory
* @param parent top-level repository
* @throws java.io.IOException if canonical path couldn't be resolved
*/
protected FileRepository(File dir, FileRepository parent) throws IOException {
// make sure our directory has an absolute path,
// see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4117557
directory = dir; //dir.getCanonicalFile();
this.parent = parent;
// We intentionally get the name from original file,
// canonical path's file name may be different in case of symlink.
name = dir.getName();
path = directory.getPath();
if (!path.endsWith(File.separator)) {
path += File.separator;
}
}
/**
* Check whether the repository exists.
*
* @return true if the repository exists.
*/
public boolean exists() {
return directory.isDirectory();
}
/**
* Create a child repository with the given name
*
* @param name the name of the repository
* @return the child repository
*/
public AbstractRepository createChildRepository(String name) throws IOException {
File f = new File(directory, name);
return new FileRepository(f, this);
}
/**
* Get this repository's parent repository.
*/
@Override
public AbstractRepository getParentRepository() {
if (parent == null) {
// allow to escape file repository root
try {
SoftReference<AbstractRepository> ref = repositories.get("..");
AbstractRepository repo = ref == null ? null : ref.get();
if (repo == null) {
repo = new FileRepository(directory.getParentFile());
repo.setAbsolute(true);
repositories.put("..", new SoftReference<AbstractRepository>(repo));
}
return repo;
} catch (IOException iox) {
// fall through
}
}
return parent;
}
/**
* Returns the date the repository was last modified.
*
* @return last modified date
*/
public long lastModified() {
return directory.lastModified();
}
/**
* Checksum of the repository and all its contained resources. Implementations
* should make sure to return a different checksum if any contained resource
* has changed.
*
* @return checksum
*/
public synchronized long getChecksum() throws IOException {
// delay checksum check if already checked recently
if (System.currentTimeMillis() > lastChecksumTime + cacheTime) {
// FIXME
long checksum = lastModified;
for (Resource res : resources.values()) {
checksum += res.lastModified();
}
lastChecksum = checksum;
lastChecksumTime = System.currentTimeMillis();
}
return lastChecksum;
}
/**
* Called to create a child resource for this repository
*/
@Override
protected Resource lookupResource(String name) throws IOException {
AbstractResource res = resources.get(name);
if (res == null) {
res = new FileResource(new File(directory, name), this);
resources.put(name, res);
}
return res;
}
protected void getResources(final List<Resource> list, final boolean recursive)
throws IOException {
final File[] dir = directory.listFiles();
for (final File file : dir) {
if (file.isFile()) {
final Resource resource = this.lookupResource(file.getName());
list.add(resource);
} else if (recursive && file.isDirectory()) {
AbstractRepository repo = lookupRepository(file.getName());
repo.getResources(list, true);
}
}
}
public Repository[] getRepositories() throws IOException {
final File[] dir = directory.listFiles();
final List<Repository> list = new ArrayList<Repository>(dir.length);
for (final File file : dir) {
if (file.isDirectory()) {
list.add(lookupRepository(file.getName()));
}
}
return list.toArray(new Repository[list.size()]);
}
public URL getUrl() throws MalformedURLException {
// Trailing slash on directories is required for ClassLoaders
return new URL("file:" + path);
}
@Override
public int hashCode() {
return 17 + (37 * directory.hashCode());
}
@Override
public boolean equals(Object obj) {
return obj instanceof FileRepository &&
directory.equals(((FileRepository) obj).directory);
}
@Override
public String toString() {
return new StringBuffer("FileRepository[").append(path).append("]").toString();
}
}