package railo.commons.io.res.type.file;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import railo.commons.cli.Command;
import railo.commons.io.IOUtil;
import railo.commons.io.ModeUtil;
import railo.commons.io.SystemUtil;
import railo.commons.io.res.ContentType;
import railo.commons.io.res.Resource;
import railo.commons.io.res.ResourceProvider;
import railo.commons.io.res.filter.ResourceFilter;
import railo.commons.io.res.filter.ResourceNameFilter;
import railo.commons.io.res.util.ResourceOutputStream;
import railo.commons.io.res.util.ResourceUtil;
/**
* Implementation og Resource for the local filesystem (java.io.File)
*/
public final class FileResource extends File implements Resource {
private final FileResourceProvider provider;
/**
* Constructor for the factory
* @param pathname
*/
FileResource(FileResourceProvider provider,String pathname) {
super(pathname);
this.provider=provider;
}
/**
* Inner Constr constructor to create parent/child
* @param parent
* @param child
*/
private FileResource(FileResourceProvider provider,File parent, String child) {
super(parent, child);
this.provider=provider;
}
@Override
public void copyFrom(Resource res,boolean append) throws IOException {
IOUtil.copy(res, this.getOutputStream(append),true);
}
@Override
public void copyTo(Resource res,boolean append) throws IOException {
IOUtil.copy(this, res.getOutputStream(append),true);
}
@Override
public Resource getAbsoluteResource() {
return new FileResource(provider,getAbsolutePath());
}
@Override
public Resource getCanonicalResource() throws IOException {
return new FileResource(provider,getCanonicalPath());
}
@Override
public Resource getParentResource() {
String p = getParent();
if(p==null) return null;
return new FileResource(provider,p);
}
@Override
public Resource[] listResources() {
String[] files = list();
if(files==null) return null;
Resource[] resources=new Resource[files.length];
for(int i=0;i<files.length;i++) {
resources[i]=getRealResource(files[i]);
}
return resources;
}
@Override
public String[] list(ResourceFilter filter) {
String[] files = list();
if(files==null) return null;
List list=new ArrayList();
FileResource res;
for(int i=0;i<files.length;i++) {
res=new FileResource(provider,this,files[i]);
if(filter.accept(res))list.add(files[i]);
}
return (String[]) list.toArray(new String[list.size()]);
}
@Override
public Resource[] listResources(ResourceFilter filter) {
String[] files = list();
if(files==null) return null;
List list=new ArrayList();
Resource res;
for(int i=0;i<files.length;i++) {
res=getRealResource(files[i]);
if(filter.accept(res))list.add(res);
}
return (Resource[]) list.toArray(new FileResource[list.size()]);
}
@Override
public String[] list(ResourceNameFilter filter) {
String[] files = list();
if(files==null) return null;
List list=new ArrayList();
for(int i=0;i<files.length;i++) {
if(filter.accept(this,files[i]))list.add(files[i]);
}
return (String[]) list.toArray(new String[list.size()]);
}
@Override
public Resource[] listResources(ResourceNameFilter filter) {
String[] files = list();
if(files==null) return null;
List list=new ArrayList();
for(int i=0;i<files.length;i++) {
if(filter.accept(this,files[i]))list.add(getRealResource(files[i]));
}
return (Resource[]) list.toArray(new Resource[list.size()]);
}
@Override
public void moveTo(Resource dest) throws IOException {
if(this.equals(dest)) return;
boolean done=false;
if(dest instanceof File) {
provider.lock(this);
try {
if(dest.exists() && !dest.delete())
throw new IOException("can't move file "+this.getAbsolutePath()+" cannot remove existing file "+dest.getAbsolutePath());
done=super.renameTo((File)dest);
/*if(!super.renameTo((File)dest)) {
throw new IOException("can't move file "+this.getAbsolutePath()+" to destination resource "+dest.getAbsolutePath());
}*/
}
finally {
provider.unlock(this);
}
}
if(!done) {
ResourceUtil.checkMoveToOK(this, dest);
IOUtil.copy(getInputStream(),dest,true);
if(!this.delete()) {
throw new IOException("can't delete resource "+this.getAbsolutePath());
}
}
}
@Override
public InputStream getInputStream() throws IOException {
//provider.lock(this);
provider.read(this);
try {
//return new BufferedInputStream(new ResourceInputStream(this,new FileInputStream(this)));
return new BufferedInputStream(new FileInputStream(this));
}
catch(IOException ioe) {
//provider.unlock(this);
throw ioe;
}
}
@Override
public OutputStream getOutputStream() throws IOException {
return getOutputStream(false);
}
@Override
public OutputStream getOutputStream(boolean append) throws IOException {
provider.lock(this);
try {
if(!super.exists() && !super.createNewFile()) {
throw new IOException("can't create file "+this);
}
return new BufferedOutputStream(new ResourceOutputStream(this,new FileOutputStream(this,append)));
}
catch(IOException ioe) {
provider.unlock(this);
throw ioe;
}
}
@Override
public void createFile(boolean createParentWhenNotExists) throws IOException {
provider.lock(this);
try {
if(createParentWhenNotExists) {
File p = super.getParentFile();
if(!p.exists()) p.mkdirs();
}
if(!super.createNewFile()) {
if(super.isFile()) throw new IOException("can't create file "+this+", file already exists");
throw new IOException("can't create file "+this);
}
}
finally {
provider.unlock(this);
}
}
@Override
public void remove(boolean alsoRemoveChildren) throws IOException {
if(alsoRemoveChildren && isDirectory()) {
Resource[] children = listResources();
for(int i=0;i<children.length;i++) {
children[i].remove(alsoRemoveChildren);
}
}
provider.lock(this);
try {
if(!super.delete()) {
if(!super.exists())throw new IOException("can't delete file "+this+", file does not exist");
if(!super.canWrite())throw new IOException("can't delete file "+this+", no access");
throw new IOException("can't delete file "+this);
}
}
finally {
provider.unlock(this);
}
}
@Override
public String getReal(String realpath) {
if(realpath.length()<=2) {
if(realpath.length()==0) return getPath();
if(realpath.equals(".")) return getPath();
if(realpath.equals("..")) return getParent();
}
return new FileResource(provider,this,realpath).getPath();
}
@Override
public Resource getRealResource(String realpath) {
if(realpath.length()<=2) {
if(realpath.length()==0) return this;
if(realpath.equals(".")) return this;
if(realpath.equals("..")) return getParentResource();
}
return new FileResource(provider,this,realpath);
}
public ContentType getContentType() {
return ResourceUtil.getContentType(this);
}
@Override
public void createDirectory(boolean createParentWhenNotExists) throws IOException {
provider.lock(this);
try {
if(createParentWhenNotExists?!_mkdirs():!super.mkdir()) {
if(super.isDirectory()) throw new IOException("can't create directory "+this+", directory already exists");
throw new IOException("can't create directory "+this);
}
}
finally {
provider.unlock(this);
}
}
@Override
public ResourceProvider getResourceProvider() {
return provider;
}
@Override
public boolean isReadable() {
return canRead();
}
@Override
public boolean isWriteable() {
return canWrite();
}
@Override
public boolean renameTo(Resource dest) {
try {
moveTo(dest);
return true;
}
catch (IOException e) {}
return false;
}
@Override
public boolean isArchive() {
return getAttribute(ATTRIBUTE_ARCHIVE);
}
@Override
public boolean isSystem() {
return getAttribute(ATTRIBUTE_SYSTEM);
}
@Override
public int getMode() {
if(!exists()) return 0;
if(SystemUtil.isUnix()) {
try {
// TODO geht nur fuer file
String line = Command.execute("ls -ld "+getPath(),false).getOutput();
line=line.trim();
line=line.substring(0,line.indexOf(' '));
//print.ln(getPath());
return ModeUtil.toOctalMode(line);
} catch (Exception e) {}
}
int mode=SystemUtil.isWindows() && exists() ?0111:0;
if(super.canRead())mode+=0444;
if(super.canWrite())mode+=0222;
return mode;
}
public void setMode(int mode) throws IOException {
// TODO unter windows mit setReadable usw.
if(!SystemUtil.isUnix()) return;
provider.lock(this);
try {
//print.ln(ModeUtil.toStringMode(mode));
if (Runtime.getRuntime().exec(
new String[] { "chmod", ModeUtil.toStringMode(mode), getPath() } ).waitFor() != 0)
throw new IOException("chmod "+ModeUtil.toStringMode(mode)+" " + toString() + " failed");
}
catch (InterruptedException e) {
throw new IOException("Interrupted waiting for chmod " + toString());
}
finally {
provider.unlock(this);
}
}
@Override
public void setArchive(boolean value) throws IOException {
setAttribute(ATTRIBUTE_ARCHIVE, value);
}
@Override
public void setHidden(boolean value) throws IOException {
setAttribute(ATTRIBUTE_HIDDEN, value);
}
@Override
public void setSystem(boolean value) throws IOException {
setAttribute(ATTRIBUTE_SYSTEM, value);
}
@Override
public boolean setReadable(boolean value) {
if(!SystemUtil.isUnix()) return false;
try {
setMode(ModeUtil.setReadable(getMode(), value));
return true;
}
catch (IOException e) {
return false;
}
}
public boolean setWritable(boolean value) {
// setReadonly
if(!value){
try {
provider.lock(this);
if(!super.setReadOnly())
throw new IOException("can't set resource read-only");
}
catch(IOException ioe){
return false;
}
finally {
provider.unlock(this);
}
return true;
}
if(SystemUtil.isUnix()) {
// need no lock because get/setmode has one
try {
setMode(ModeUtil.setWritable(getMode(), value));
}
catch (IOException e) {
return false;
}
return true;
}
try {
provider.lock(this);
Runtime.getRuntime().exec("attrib -R " + getAbsolutePath());
}
catch(IOException ioe){
return false;
}
finally {
provider.unlock(this);
}
return true;
}
@Override
public boolean createNewFile() {
try {
provider.lock(this);
return super.createNewFile();
}
catch (IOException e) {
return false;
}
finally {
provider.unlock(this);
}
}
@Override
public boolean canRead() {
try {
provider.read(this);
} catch (IOException e) {
return false;
}
return super.canRead();
}
@Override
public boolean canWrite() {
try {
provider.read(this);
} catch (IOException e) {
return false;
}
return super.canWrite();
}
@Override
public boolean delete() {
try {
provider.lock(this);
return super.delete();
}
catch (IOException e) {
return false;
}
finally {
provider.unlock(this);
}
}
@Override
public boolean exists() {
try {
provider.read(this);
} catch (IOException e) {}
return super.exists();
}
@Override
public boolean isAbsolute() {
try {
provider.read(this);
}
catch (IOException e) {
return false;
}
return super.isAbsolute();
}
@Override
public boolean isDirectory() {
try {
provider.read(this);
} catch (IOException e) {
return false;
}
return super.isDirectory();
}
@Override
public boolean isFile() {
try {
provider.read(this);
} catch (IOException e) {
return false;
}
return super.isFile();
}
@Override
public boolean isHidden() {
try {
provider.read(this);
} catch (IOException e) {
return false;
}
return super.isHidden();
}
@Override
public long lastModified() {
try {
provider.read(this);
} catch (IOException e) {
return 0;
}
return super.lastModified();
}
@Override
public long length() {
try {
provider.read(this);
} catch (IOException e) {
return 0;
}
return super.length();
}
@Override
public String[] list() {
try {
provider.read(this);
} catch (IOException e) {
return null;
}
return super.list();
}
@Override
public boolean mkdir() {
try {
provider.lock(this);
return super.mkdir();
}
catch (IOException e) {
return false;
}
finally {
provider.unlock(this);
}
}
@Override
public boolean mkdirs() {
try {
provider.lock(this);
return _mkdirs();
}
catch (IOException e) {
return false;
}
finally {
provider.unlock(this);
}
}
private boolean _mkdirs() {
if (super.exists()) return false;
if (super.mkdir()) return true;
File parent = super.getParentFile();
return (parent != null) && (parent.mkdirs() && super.mkdir());
}
@Override
public boolean setLastModified(long time) {
try {
provider.lock(this);
return super.setLastModified(time);
}
catch (Throwable t) {// IllegalArgumentException or IOException
return false;
}
finally {
provider.unlock(this);
}
}
@Override
public boolean setReadOnly() {
try {
provider.lock(this);
return super.setReadOnly();
}
catch (IOException e) {
return false;
}
finally {
provider.unlock(this);
}
}
public boolean getAttribute(short attribute) {
if(!SystemUtil.isWindows()) return false;
if(attribute==ATTRIBUTE_HIDDEN) return isHidden();
String attr=null;
if(attribute==ATTRIBUTE_ARCHIVE) attr="A";
else if(attribute==ATTRIBUTE_SYSTEM) attr="S";
try {
provider.lock(this);
String result = Command.execute("attrib " + getAbsolutePath(),false).getOutput();
String[] arr = railo.runtime.type.util.ListUtil.listToStringArray(result, ' ');
for(int i=0;i>arr.length-1;i++) {
if(attr.equals(arr[i].toUpperCase())) return true;
}
}
catch (Exception e) {}
finally {
provider.unlock(this);
}
return false;
}
public void setAttribute(short attribute, boolean value) throws IOException {
String attr=null;
if(attribute==ATTRIBUTE_ARCHIVE) attr="A";
else if(attribute==ATTRIBUTE_HIDDEN) attr="H";
else if(attribute==ATTRIBUTE_SYSTEM) attr="S";
if(!SystemUtil.isWindows()) return ;
provider.lock(this);
try {
Runtime.getRuntime().exec("attrib "+attr+(value?"+":"-")+" " + getAbsolutePath());
}
finally {
provider.unlock(this);
}
}
public boolean equals(Object other){
if(provider.isCaseSensitive()) return super.equals(other);
if(!(other instanceof File)) return false;
return getAbsolutePath().equalsIgnoreCase(((File)other).getAbsolutePath());
}
}