package jj.resource;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import jj.util.SHA1Helper;
/**
* <p>
* Provides base services for {@link FileResource} instances.
*
* @author jason
*
*/
public abstract class AbstractFileResource<T> extends AbstractResource<T> implements FileResource<T> {
protected final Path path;
protected final FileTime lastModified;
protected final long size;
protected final ByteBuf byteBuffer;
protected final String sha1;
@ResourceThread
protected AbstractFileResource(
final Dependencies dependencies,
final Path path
) {
this(dependencies, path, true);
}
// java likes to be a pain
@SuppressWarnings("unchecked")
private static NoSuchResourceException noSuchResourceException(Class<?> instanceClass, Path path) {
throw new NoSuchResourceException((Class<? extends Resource<?>>)instanceClass, path);
}
@ResourceThread
protected AbstractFileResource(
final Dependencies dependencies,
final Path path,
final boolean keepBytes
) {
super(dependencies);
try {
BasicFileAttributes attributes;
try {
attributes = Files.readAttributes(path, BasicFileAttributes.class);
} catch (NoSuchFileException nsfe) {
throw noSuchResourceException(getClass(), path);
}
if (!attributes.isRegularFile()) {
throw noSuchResourceException(getClass(), path);
}
size = attributes.size();
boolean large = size > resourceConfiguration.maxFileSizeToLoad();
if (large && keepBytes) {
throw new ResourceNotViableException(path,
"resource is " + size + " bytes but configured maximum is " +
resourceConfiguration.maxFileSizeToLoad() + " bytes"
);
}
this.path = path;
this.lastModified = attributes.lastModifiedTime();
if (keepBytes) { // read it all in
byteBuffer = readAllBytes(path);
sha1 = SHA1Helper.keyFor(byteBuffer);
// read the SHA-1 directly if the size is under the configured limit
// or we're getting it from a jar
} else if (!large || path.getFileSystem() != FileSystems.getDefault()) {
byteBuffer = null;
sha1 = SHA1Helper.keyFor(path);
} else { // avoid reading the sha1 directly, try to save it
byteBuffer = null;
Sha1Resource sha1Resource = resourceFinder.loadResource(Sha1Resource.class, base(), name(), new Sha1ResourceTarget(this));
assert sha1Resource.representedFileSize() == size;
sha1 = sha1Resource.representedSha();
}
} catch (IOException ioe) {
throw new ResourceNotViableException(path, ioe);
}
}
@Override
protected String extension() {
// file resources get a path based on their name, right?
// at least in all cases where these settings matter, anyway
String name = name();
return name.substring(name.lastIndexOf(".") + 1);
}
private ByteBuf readAllBytes(final Path path) throws IOException {
return Unpooled.wrappedBuffer(Files.readAllBytes(path));
}
@Override
public String sha1() {
return sha1;
}
@Override
public Path path() {
return path;
}
@Override
public boolean isDirectory() {
return false;
}
@Override
public Charset charset() {
return settings.charset();
}
@Override
public long size() {
return size;
}
@Override
@ResourceThread
public boolean needsReplacing() throws IOException {
return (path.getFileSystem() == FileSystems.getDefault()) && lastModified.compareTo(Files.getLastModifiedTime(path)) < 0;
}
public FileTime lastModified() {
return lastModified;
}
}