/*********************************************************************************
* TotalCross Software Development Kit *
* Copyright (C) 2000-2012 SuperWaba Ltda. *
* All Rights Reserved *
* *
* This library and virtual machine is distributed in the hope that it will *
* be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 *
* A copy of this license is located in file license.txt at the root of this *
* SDK or can be downloaded here: *
* http://www.gnu.org/licenses/lgpl-3.0.txt *
* *
*********************************************************************************/
package totalcross.io;
import totalcross.sys.*;
import totalcross.util.*;
public class File4D extends RandomAccessStream
{
protected String path;
Object fileRef;
int mode = INVALID;
int slot;
boolean dontFinalize;
static final boolean isAndroid = Settings.platform.equals(Settings.ANDROID);
static final boolean isIOS = Settings.isIOS();
static final boolean isWP8 = Settings.platform.equals(Settings.WINDOWSPHONE);
static final boolean isWin32 = Settings.platform.equals(Settings.WIN32);
static final int INVALID = 0;
public static final int DONT_OPEN = 1;
public static final int READ_WRITE = 2;
public static final int READ_ONLY = 3;
public static final int CREATE = 4;
public static final int CREATE_EMPTY = 5;
public static final byte TIME_ALL = (byte) 0xF;
public static final byte TIME_CREATED = (byte) 1;
public static final byte TIME_MODIFIED = (byte) 2;
public static final byte TIME_ACCESSED = (byte) 4;
public static final int ATTR_ARCHIVE = 1;
public static final int ATTR_HIDDEN = 2;
public static final int ATTR_READ_ONLY = 4;
public static final int ATTR_SYSTEM = 8;
public static String[] winceVols = {"\\Storage Card2\\", "\\Storage Card1\\", "\\SD Card\\", "\\Storage Card\\",
"\\SD-MMCard\\", "\\CF Card\\"}; // guich@572_3
private static String deviceAlias;
native private static String getDeviceAlias();
public File4D(String path, int mode, int slot) throws totalcross.io.IllegalArgumentIOException, totalcross.io.FileNotFoundException, totalcross.io.IOException
{
if (deviceAlias == null)
deviceAlias = getDeviceAlias();
if (mode == 8) mode = CREATE_EMPTY; // keep compatibility
if (path == null)
throw new java.lang.NullPointerException("Argument 'path' cannot have a null value.");
if (path.length() == 0 || path.length() > 255)
throw new totalcross.io.IllegalArgumentIOException("path", path);
if (mode < DONT_OPEN || mode > CREATE_EMPTY)
throw new totalcross.io.IllegalArgumentIOException("mode", Convert.toString(mode));
if (slot < -1)
throw new totalcross.io.IllegalArgumentIOException("slot", Convert.toString(slot));
path = Convert.normalizePath(path);
if (path.startsWith("device/")) // flsobral@tc110_108: added support for the alias "device/".
{
path = path.substring(6);
//flsobral@tc129.2: path for both iphone and ipad.
if (Settings.isIOS()) // guich@tc115_14: as per greg's suggestion
path = Convert.appendPath(deviceAlias, path);
else if (isAndroid || isWP8 || isWin32)
path = Settings.appPath + "/" + path;
slot = 1;
}
else
if (isAndroid && !path.startsWith("/sdcard") && !path.startsWith("/mnt") && path.indexOf("data/data") < 0) // in android, force access appPath if specifying the root
path = Settings.appPath+"/"+path;
else
if (isIOS && !path.startsWith("/"))
path = Convert.appendPath(deviceAlias, path);
this.path = path;
this.mode = mode;
this.slot = slot;
create(path, mode, slot);
}
public File4D(String path, int mode) throws totalcross.io.IllegalArgumentIOException, totalcross.io.FileNotFoundException, totalcross.io.IOException
{
this(path, mode, -1);
}
public File4D(String path) throws IllegalArgumentIOException, totalcross.io.IOException
{
this(path, DONT_OPEN, -1);
}
public void close() throws totalcross.io.IOException
{
if (mode == INVALID)
throw new totalcross.io.IOException("Invalid file object");
if (mode != DONT_OPEN)
nativeClose();
}
public String getPath()
{
return path;
}
public int getPos() throws totalcross.io.IOException
{
if (mode == INVALID)
throw new IOException("Invalid file handler");
if (mode == DONT_OPEN)
throw new IOException("Operation cannot be used in DONT_OPEN mode");
return pos;
}
public void setPos(int offset, int origin) throws IOException
{
if (mode <= DONT_OPEN)
throw new IOException(mode == INVALID ? "Invalid file handler" : "Operation cannot be used in mode DONT_OPEN");
int newPos;
int length = this.getSize();
switch (origin)
{
case SEEK_SET: newPos = offset; break;
case SEEK_CUR: newPos = this.pos + offset; break;
case SEEK_END: newPos = length + offset - 1; break;
default: throw new IllegalArgumentException("origin");
}
if (newPos < 0)
throw new IOException("Invalid position: "+newPos);
setPos(newPos);
}
native private void create(String path, int mode, int slot) throws totalcross.io.FileNotFoundException, totalcross.io.IOException;
native private void nativeClose() throws totalcross.io.IOException;
native public static boolean isCardInserted(int slot) throws totalcross.io.IllegalArgumentIOException;
native public void createDir() throws totalcross.io.IOException; // guich@tc122_24: void, not boolean
native public void delete() throws totalcross.io.FileNotFoundException, totalcross.io.IOException;
native public boolean exists() throws totalcross.io.IOException;
native public int getSize() throws totalcross.io.IOException;
native public boolean isDir() throws totalcross.io.IOException;
native public String[] listFiles() throws totalcross.io.IOException;
native public int readBytes(byte b[], int off, int len) throws totalcross.io.IOException;
native public void rename(String path) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException;
native public void setPos(int pos) throws totalcross.io.IOException;
native public int writeBytes(byte b[], int off, int len) throws totalcross.io.IOException;
native public void setAttributes(int attr) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException;
native public int getAttributes() throws totalcross.io.IOException;
native public void setTime(byte whichTime, totalcross.sys.Time time) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException;
native public totalcross.sys.Time getTime(byte whichTime) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException;
native public void setSize(int newSize) throws totalcross.io.IOException;
native public static String getCardSerialNumber(int slot) throws totalcross.io.IllegalArgumentIOException, totalcross.io.IOException;
native public boolean isEmpty() throws IOException;
public File getParent() throws totalcross.io.IOException
{
if (mode == INVALID)
throw new totalcross.io.IOException("Invalid file handler");
if (path.equals("/"))
return null;
return new File(path.substring(0, path.lastIndexOf('/')), DONT_OPEN);
}
public static File getCardVolume() throws totalcross.io.IOException
{
if (Settings.isWindowsDevice()) //flsobral@tc112_10: Fixed to also work on devices recognized as WindowsMobile.
{
String cardName = null;
try
{
cardName = Registry.getString(Registry.HKEY_LOCAL_MACHINE, "System\\StorageManager\\Profiles\\SDMemory", "Folder"); //flsobral@tc112_15: Attempt to retrieve the name from the registry.
}
catch (Exception e)
{
File f;
for (int i = winceVols.length - 1; i >= 0; i--)
try
{
if ((f = new File(winceVols[i])).isDir())
return f;
}
catch (FileNotFoundException fnfe)
{
}
}
if (cardName != null)
{
cardName = cardName.replace('\\', '/'); //flsobral@tc113_15: Always wrap the path with slashes.
if (cardName.charAt(0) != '/')
cardName = "/" + cardName;
if (!cardName.endsWith("/"))
cardName = cardName + "/";
File cardFile = new File(cardName);
if (cardFile.isDir())
return cardFile;
}
}
return null;
}
native public void flush() throws totalcross.io.IOException;
protected void finalize()
{
try
{
if (mode != INVALID)
this.close();
}
catch (totalcross.io.IOException e)
{
}
}
public int getSlot()
{
return slot;
}
private static void listFiles(String dir, Vector files, boolean recursive) throws IOException // guich@tc115_92
{
String[] list = new File(dir).listFiles();
if (list != null)
for (int i =0; i < list.length; i++)
{
String p = list[i];
String full = Convert.appendPath(dir,p);
files.addElement(full);
if (recursive && p.endsWith("/"))
listFiles(full, files, recursive);
}
}
public static String[] listFiles(String dir) throws IOException // guich@tc115_92
{
return listFiles(dir, true);
}
public static String[] listFiles(String dir, boolean recursive) throws IOException
{
Vector files = new Vector(50);
dir = Convert.appendPath(dir, "/");
files.addElement(dir);
listFiles(dir, files, recursive);
files.qsort();
return (String[]) files.toObjectArray();
}
public static void deleteDir(String dir) throws IOException // guich@tc115_92
{
String[] files = listFiles(dir);
for (int i = files.length; --i >= 0;)
new File((String)files[i]).delete();
}
native public static String[] listRoots();
public void copyTo(File dest) throws IOException // guich@tc126_8
{
setPos(0);
try {dest.setPos(0);} catch (IOException ioe) {}
byte[] buf = new byte[4096];
int n = 0;
while ((n=readBytes(buf, 0, buf.length)) > 0)
dest.writeBytes(buf,0,n);
}
public void moveTo(File dest) throws IOException // guich@tc126_8
{
if (mode == READ_ONLY)
throw new IOException("Operation cannot be used in READ_ONLY mode");
copyTo(dest);
delete();
}
native public int chmod(int mod) throws IOException; // guich@tc126_16
public static void copy(String src, String dst) throws IOException // guich@tc126_43
{
File fin=null,fout=null;
try
{
fin = new File(src,File.READ_ONLY);
fout = new File(dst,File.CREATE_EMPTY);
fin.copyTo(fout);
}
finally
{
try {if (fin != null) fin.close();} catch (Exception e) {}
try {if (fout != null) fout.close();} catch (Exception e) {}
}
}
public static void move(String src, String dst) throws IOException // guich@tc126_43
{
File fin=null,fout=null;
try
{
fin = new File(src,File.READ_WRITE);
fout = new File(dst,File.CREATE_EMPTY);
fin.moveTo(fout);
}
finally
{
try {if (fout != null) fout.close();} catch (Exception e) {}
}
}
public byte[] readAndClose() throws IOException
{
byte[] ret = read();
close();
return ret;
}
public byte[] read() throws IOException
{
int len = getSize();
byte[] ret = new byte[len];
readBytes(ret,0,len);
return ret;
}
public void writeAndClose(byte[] bytes) throws IOException
{
writeBytes(bytes, 0, bytes.length);
close();
}
}