/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.module.fs;
import gw.config.CommonServices;
import gw.fs.IDirectory;
import gw.fs.IDirectoryUtil;
import gw.fs.IFile;
import gw.fs.IResource;
import gw.lang.reflect.module.IFileSystem;
import java.io.IOException;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
public class JavaDirectoryImpl extends JavaResourceImpl implements IDirectory {
private FileRetrievalStrategy _fileRetrievalStrategy;
public JavaDirectoryImpl(File file, IFileSystem.CachingMode cachingMode) {
super(file);
setCachingMode(cachingMode);
}
public void setCachingMode(IFileSystem.CachingMode cachingMode) {
switch (cachingMode) {
case NO_CACHING:
_fileRetrievalStrategy = new UncachedFileRetrievalStrategy();
break;
case CHECK_TIMESTAMPS:
_fileRetrievalStrategy = new TimestampBasedCachingFileRetrievalStrategy();
break;
case FUZZY_TIMESTAMPS:
_fileRetrievalStrategy = new FuzzyTimestampCachingFileRetrievalStrategy();
break;
case FULL_CACHING:
_fileRetrievalStrategy = new FullyCachedFileRetrievalStrategy();
break;
default:
throw new IllegalStateException("Unrecognized caching mode " + cachingMode);
}
}
@Override
public void clearCaches() {
if (_fileRetrievalStrategy instanceof CachingFileRetrievalStrategy) {
synchronized (FileSystemImpl.CACHED_FILE_SYSTEM_LOCK) {
((CachingFileRetrievalStrategy) _fileRetrievalStrategy).clearCache();
}
}
}
@Override
public IDirectory dir(String relativePath) {
// try {
File subDir = new File(this._file, relativePath)/*.getCanonicalFile()*/;
return CommonServices.getFileSystem().getIDirectory(subDir);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
}
@Override
public IFile file(String path) {
// try {
File subFile = new File(this._file, path)/*.getCanonicalFile()*/;
return CommonServices.getFileSystem().getIFile(subFile);
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
}
@Override
public boolean mkdir() throws IOException {
return _file.mkdir();
}
@Override
public List<? extends IDirectory> listDirs() {
return _fileRetrievalStrategy.listDirs();
}
@Override
public List<? extends IFile> listFiles() {
return _fileRetrievalStrategy.listFiles();
}
@Override
public String relativePath(IResource resource) {
return IDirectoryUtil.relativePath(this, resource);
}
@Override
public boolean exists() {
return _file.isDirectory();
}
private interface FileRetrievalStrategy {
List<? extends IDirectory> listDirs();
List<? extends IFile> listFiles();
}
private class UncachedFileRetrievalStrategy implements FileRetrievalStrategy {
@Override
public List<? extends IDirectory> listDirs() {
List<IDirectory> results = new ArrayList<IDirectory>();
File[] files = _file.listFiles();
if (files != null) {
for (File f : _file.listFiles()) {
if (FileSystemImpl.isDirectory(f)) {
results.add(CommonServices.getFileSystem().getIDirectory(f));
}
}
}
return results;
}
@Override
public List<? extends IFile> listFiles() {
List<IFile> results = new ArrayList<IFile>();
File[] files = _file.listFiles();
if (files != null) {
for (File f : files) {
if (!FileSystemImpl.isDirectory(f)) {
results.add(CommonServices.getFileSystem().getIFile(f));
}
}
}
return results;
}
}
private abstract class CachingFileRetrievalStrategy implements FileRetrievalStrategy {
protected List<IDirectory> _directories;
protected List<IFile> _files;
public void clearCache() {
// This should always be called with the CACHED_FILE_SYSTEM_LOCK monitor already acquired
_directories = null;
_files = null;
}
@Override
public List<IDirectory> listDirs() {
synchronized (FileSystemImpl.CACHED_FILE_SYSTEM_LOCK) {
refreshIfNecessary();
return _directories;
}
}
@Override
public List<IFile> listFiles() {
synchronized (FileSystemImpl.CACHED_FILE_SYSTEM_LOCK) {
refreshIfNecessary();
return _files;
}
}
protected void refreshInfo() {
_files = new ArrayList<IFile>();
_directories = new ArrayList<IDirectory>();
File javaFile = toJavaFile();
maybeSetTimestamp(javaFile);
File[] files = javaFile.listFiles();
if (files != null) {
for (File f : files) {
if (FileSystemImpl.isDirectory(f)) {
_directories.add(CommonServices.getFileSystem().getIDirectory(f));
} else {
_files.add(CommonServices.getFileSystem().getIFile(f));
}
}
}
if (_directories.isEmpty()) {
_directories = Collections.emptyList();
} else {
((ArrayList) _directories).trimToSize();
}
if (_files.isEmpty()) {
_files = Collections.emptyList();
} else {
((ArrayList) _files).trimToSize();
}
}
protected abstract void refreshIfNecessary();
protected abstract void maybeSetTimestamp(File javaFile);
}
private class TimestampBasedCachingFileRetrievalStrategy extends CachingFileRetrievalStrategy {
private long _lastTimestamp;
public void clearCache() {
// This should always be called with the CACHED_FILE_SYSTEM_LOCK monitor already acquired
super.clearCache();
_lastTimestamp = -1;
}
protected void refreshIfNecessary() {
if (_lastTimestamp == -1) {
refreshInfo();
} else {
File file = toJavaFile();
long currentTimestamp = file.lastModified();
if (currentTimestamp == 0) {
// If the timestamp is 0, assume it's been deleted
_files = Collections.emptyList();
_directories = Collections.emptyList();
} else if (_lastTimestamp != currentTimestamp) {
refreshInfo();
}
}
}
@Override
protected void maybeSetTimestamp(File javaFile) {
_lastTimestamp = javaFile.lastModified();
}
}
private class FuzzyTimestampCachingFileRetrievalStrategy extends CachingFileRetrievalStrategy {
private long _lastFileTimestamp; // in ms, absolute time
private long _lastRefreshTimestamp; // in ms, absolute time
public void clearCache() {
// This should always be called with the CACHED_FILE_SYSTEM_LOCK monitor already acquired
super.clearCache();
_lastFileTimestamp = -1;
_lastRefreshTimestamp = -1;
}
protected void refreshIfNecessary() {
if (_lastFileTimestamp == -1) {
doRefreshImpl();
} else {
File file = toJavaFile();
long currentTimestamp = file.lastModified();
if (currentTimestamp == 0) {
// If the timestamp is 0, assume it's been deleted
_files = Collections.emptyList();
_directories = Collections.emptyList();
} else if (_lastFileTimestamp != currentTimestamp) {
doRefreshImpl();
} else {
long refreshDelta = _lastRefreshTimestamp - currentTimestamp;
if(refreshDelta > -16 && refreshDelta < 16) {
doRefreshImpl();
}
}
}
}
private void doRefreshImpl() {
_lastRefreshTimestamp = System.currentTimeMillis();
refreshInfo();
}
@Override
protected void maybeSetTimestamp(File javaFile) {
_lastFileTimestamp = javaFile.lastModified();
}
}
private class FullyCachedFileRetrievalStrategy extends CachingFileRetrievalStrategy {
@Override
protected void refreshIfNecessary() {
if (_files == null) {
refreshInfo();
}
}
@Override
protected void maybeSetTimestamp(File javaFile) {
// Do nothing
}
}
}