Return-Path: X-Original-To: apmail-cordova-commits-archive@www.apache.org Delivered-To: apmail-cordova-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 1A42C10ADE for ; Mon, 10 Feb 2014 23:26:41 +0000 (UTC) Received: (qmail 79220 invoked by uid 500); 10 Feb 2014 23:23:19 -0000 Delivered-To: apmail-cordova-commits-archive@cordova.apache.org Received: (qmail 78250 invoked by uid 500); 10 Feb 2014 23:22:48 -0000 Mailing-List: contact commits-help@cordova.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cordova.apache.org Delivered-To: mailing list commits@cordova.apache.org Received: (qmail 77510 invoked by uid 99); 10 Feb 2014 23:22:29 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 10 Feb 2014 23:22:29 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 394CE922DD3; Mon, 10 Feb 2014 23:22:28 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: steven@apache.org To: commits@cordova.apache.org Date: Mon, 10 Feb 2014 23:22:42 -0000 Message-Id: <4419c38ba4824a22ae3563ca0647f804@git.apache.org> In-Reply-To: <4f6ca4f3c16b4269bc790ad4ed976363@git.apache.org> References: <4f6ca4f3c16b4269bc790ad4ed976363@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [16/50] git commit: Android: Refactor File API Android: Refactor File API Also gets content file copying working Promote Filesystem interface to base class Remove duplicated code; push to Filesystem class Stream file output where possible between methods Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/eaeb5981 Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/eaeb5981 Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/eaeb5981 Branch: refs/heads/master Commit: eaeb598124c01176c1c9191910b5761e56c00904 Parents: e7f83cb Author: Ian Clelland Authored: Wed Jan 8 13:24:50 2014 -0500 Committer: Ian Clelland Committed: Thu Jan 9 14:03:38 2014 -0500 ---------------------------------------------------------------------- src/android/ContentFilesystem.java | 148 +++++++++++++++++---------- src/android/FileUtils.java | 34 +++++-- src/android/Filesystem.java | 167 +++++++++++++++++++++++++++---- src/android/LocalFilesystem.java | 143 +++++++------------------- src/android/LocalFilesystemURL.java | 7 +- src/android/ReadFileCallback.java | 7 -- 6 files changed, 308 insertions(+), 198 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/eaeb5981/src/android/ContentFilesystem.java ---------------------------------------------------------------------- diff --git a/src/android/ContentFilesystem.java b/src/android/ContentFilesystem.java index 03a1939..535a2fe 100644 --- a/src/android/ContentFilesystem.java +++ b/src/android/ContentFilesystem.java @@ -1,26 +1,30 @@ package org.apache.cordova.file; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.InputStream; +import java.io.OutputStream; import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaResourceApi; +import org.apache.cordova.CordovaWebView; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.ContentResolver; import android.database.Cursor; +import android.net.Uri; import android.provider.MediaStore; -public class ContentFilesystem implements Filesystem { +public class ContentFilesystem extends Filesystem { private CordovaInterface cordova; + private CordovaResourceApi resourceApi; - public ContentFilesystem(CordovaInterface cordova) { + public ContentFilesystem(CordovaInterface cordova, CordovaWebView webView) { this.cordova = cordova; + this.resourceApi = new CordovaResourceApi(webView.getContext(), webView.pluginManager); } @Override @@ -41,23 +45,37 @@ public class ContentFilesystem implements Filesystem { throw new IOException(); } try { - JSONObject entry = new JSONObject(); - entry.put("isFile", fp.isFile()); - entry.put("isDirectory", fp.isDirectory()); - entry.put("name", fp.getName()); - entry.put("fullPath", "file://" + fp.getAbsolutePath()); - // The file system can't be specified, as it would lead to an infinite loop. - entry.put("filesystem", FileUtils.APPLICATION); - return entry; + return makeEntryForPath(inputURL.fullPath, inputURL.filesystemType, fp.isDirectory()); } catch (JSONException e) { throw new IOException(); } } - - @Override + + @Override public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, - String fileName, JSONObject options, boolean directory) throws IOException { - throw new IOException("Cannot create content url"); + String fileName, JSONObject options, boolean directory) throws IOException, TypeMismatchException, JSONException { + if (options != null) { + if (options.optBoolean("create")) { + throw new IOException("Cannot create content url"); + } + } + LocalFilesystemURL requestedURL = new LocalFilesystemURL(Uri.withAppendedPath(inputURL.URL, fileName)); + File fp = new File(this.filesystemPathForURL(requestedURL)); + if (!fp.exists()) { + throw new FileNotFoundException("path does not exist"); + } + if (directory) { + if (fp.isFile()) { + throw new TypeMismatchException("path doesn't exist or is file"); + } + } else { + if (fp.isDirectory()) { + throw new TypeMismatchException("path doesn't exist or is directory"); + } + } + // Return the directory + return makeEntryForPath(requestedURL.fullPath, requestedURL.filesystemType, directory); + } @Override @@ -93,55 +111,72 @@ public class ContentFilesystem implements Filesystem { @Override public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException { - // TODO Auto-generated method stub - return null; - } - - @Override - public JSONObject getParentForLocalURL(LocalFilesystemURL inputURL) - throws IOException { - LocalFilesystemURL newURL = new LocalFilesystemURL(inputURL.URL); - - if (!("".equals(inputURL.fullPath) || "/".equals(inputURL.fullPath))) { - int end = inputURL.fullPath.endsWith("/") ? 1 : 0; - int lastPathStartsAt = inputURL.fullPath.lastIndexOf('/', inputURL.fullPath.length()-end)+1; - newURL.fullPath = newURL.fullPath.substring(0,lastPathStartsAt); - } - return getEntryForLocalURL(newURL); + String path = filesystemPathForURL(inputURL); + if (path == null) { + throw new FileNotFoundException(); + } + File file = new File(path); + JSONObject metadata = new JSONObject(); + try { + metadata.put("size", file.length()); + metadata.put("type", resourceApi.getMimeType(inputURL.URL)); + metadata.put("name", file.getName()); + metadata.put("fullPath", inputURL.fullPath); + metadata.put("lastModifiedDate", file.lastModified()); + } catch (JSONException e) { + return null; + } + return metadata; } @Override public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName, Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) - throws IOException, InvalidModificationException, JSONException, - NoModificationAllowedException, FileExistsException { - // TODO Auto-generated method stub - return null; + throws IOException, InvalidModificationException, JSONException, + NoModificationAllowedException, FileExistsException { + if (LocalFilesystem.class.isInstance(srcFs)) { + /* Same FS, we can shortcut with CordovaResourceApi operations */ + // Figure out where we should be copying to + final LocalFilesystemURL destinationURL = makeDestinationURL(newName, srcURL, destURL); + + OutputStream os = resourceApi.openOutputStream(destURL.URL); + CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(srcURL.URL); + if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) { + throw new NoModificationAllowedException("Cannot move file at source URL"); + } + try { + resourceApi.copyResource(ofrr, os); + } catch (IOException e) { + throw new IOException("Cannot read file at source URL"); + } + if (move) { + srcFs.removeFileAtLocalURL(srcURL); + } + return makeEntryForURL(destinationURL, false); + } else { + // Need to copy the hard way + return super.copyFileToURL(destURL, newName, srcFs, srcURL, move); + } } + @Override - public void readFileAtURL(LocalFilesystemURL inputURL, int start, int end, + public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end, ReadFileCallback readFileCallback) throws IOException { - int numBytesToRead = end - start; - byte[] bytes = new byte[numBytesToRead]; - String contentType; - - File file = new File(this.filesystemPathForURL(inputURL)); - contentType = FileHelper.getMimeTypeForExtension(file.getAbsolutePath()); - - InputStream inputStream = new FileInputStream(file); - int numBytesRead = 0; + CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(inputURL.URL); + if (end < 0) { + end = ofrr.length; + } + long numBytesToRead = end - start; try { if (start > 0) { - inputStream.skip(start); - } - while (numBytesToRead > 0 && (numBytesRead = inputStream.read(bytes, numBytesRead, numBytesToRead)) >= 0) { - numBytesToRead -= numBytesRead; + ofrr.inputStream.skip(start); } + LimitedInputStream inputStream = new LimitedInputStream(ofrr.inputStream, numBytesToRead); + readFileCallback.handleData(inputStream, ofrr.mimeType); } finally { - inputStream.close(); + ofrr.inputStream.close(); } - readFileCallback.handleData(bytes, contentType); } @Override @@ -178,7 +213,7 @@ public class ContentFilesystem implements Filesystem { @Override public LocalFilesystemURL URLforFilesystemPath(String path) { - // TODO Auto-generated method stub + // Returns null as we don't support reverse mapping back to content:// URLs return null; } @@ -188,4 +223,11 @@ public class ContentFilesystem implements Filesystem { File file = new File(path); return file.exists(); } -} + + @Override + OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) + throws IOException { + OutputStream os = resourceApi.openOutputStream(inputURL.URL); + return os; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/eaeb5981/src/android/FileUtils.java ---------------------------------------------------------------------- diff --git a/src/android/FileUtils.java b/src/android/FileUtils.java index 20a2700..d1c1b59 100644 --- a/src/android/FileUtils.java +++ b/src/android/FileUtils.java @@ -20,7 +20,6 @@ package org.apache.cordova.file; import android.net.Uri; import android.os.Environment; -import android.provider.MediaStore; import android.util.Base64; import android.util.Log; @@ -34,9 +33,11 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.net.MalformedURLException; import java.net.URLDecoder; import java.util.ArrayList; @@ -68,6 +69,7 @@ public class FileUtils extends CordovaPlugin { public static int PERSISTENT = 1; public static int RESOURCE = 2; public static int APPLICATION = 3; + public static int CONTENT = 4; // This field exists only to support getEntry, below, which has been deprecated private static FileUtils filePlugin; @@ -99,8 +101,9 @@ public class FileUtils extends CordovaPlugin { fp.mkdirs(); this.filesystems.add(new LocalFilesystem(cordova, "temporary", tempRoot)); this.filesystems.add(new LocalFilesystem(cordova, "persistent", persistentRoot)); - this.filesystems.add(null); - this.filesystems.add(new ContentFilesystem(cordova)); + this.filesystems.add(null); // Hold for 'resource' FS + this.filesystems.add(null); // Hold for 'application' FS + this.filesystems.add(new ContentFilesystem(cordova, webView)); // Initialize static plugin reference for deprecated getEntry method if (filePlugin == null) { @@ -764,22 +767,35 @@ public class FileUtils extends CordovaPlugin { throw new MalformedURLException("No installed handlers for this URL"); } - fs.readFileAtURL(inputURL, start, end, new ReadFileCallback() { - public void handleData(byte[] bytes, String contentType) { + fs.readFileAtURL(inputURL, start, end, new Filesystem.ReadFileCallback() { + public void handleData(InputStream inputStream, String contentType) { try { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + final int BUFFER_SIZE = 8192; + byte[] buffer = new byte[BUFFER_SIZE]; + + for (;;) { + int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE); + + if (bytesRead <= 0) { + break; + } + os.write(buffer, 0, bytesRead); + } + PluginResult result; switch (resultType) { case PluginResult.MESSAGE_TYPE_STRING: - result = new PluginResult(PluginResult.Status.OK, new String(bytes, encoding)); + result = new PluginResult(PluginResult.Status.OK, os.toString(encoding)); break; case PluginResult.MESSAGE_TYPE_ARRAYBUFFER: - result = new PluginResult(PluginResult.Status.OK, bytes); + result = new PluginResult(PluginResult.Status.OK, os.toByteArray()); break; case PluginResult.MESSAGE_TYPE_BINARYSTRING: - result = new PluginResult(PluginResult.Status.OK, bytes, true); + result = new PluginResult(PluginResult.Status.OK, os.toByteArray(), true); break; default: // Base64. - byte[] base64 = Base64.encode(bytes, Base64.NO_WRAP); + byte[] base64 = Base64.encode(os.toByteArray(), Base64.NO_WRAP); String s = "data:" + contentType + ";base64," + new String(base64, "US-ASCII"); result = new PluginResult(PluginResult.Status.OK, s); } http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/eaeb5981/src/android/Filesystem.java ---------------------------------------------------------------------- diff --git a/src/android/Filesystem.java b/src/android/Filesystem.java index ba2c7bd..1f869c9 100644 --- a/src/android/Filesystem.java +++ b/src/android/Filesystem.java @@ -1,47 +1,174 @@ package org.apache.cordova.file; import java.io.FileNotFoundException; +import java.io.FilterInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; -import org.apache.cordova.CordovaInterface; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -public interface Filesystem { +public abstract class Filesystem { - JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException; + public interface ReadFileCallback { + public void handleData(InputStream inputStream, String contentType) throws IOException; + } - JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, String fileName, - JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException; + public static JSONObject makeEntryForPath(String path, int fsType, Boolean isDir) + throws JSONException { + JSONObject entry = new JSONObject(); - boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException; + int end = path.endsWith("/") ? 1 : 0; + String[] parts = path.substring(0,path.length()-end).split("/"); + String name = parts[parts.length-1]; + entry.put("isFile", !isDir); + entry.put("isDirectory", isDir); + entry.put("name", name); + entry.put("fullPath", path); + // The file system can't be specified, as it would lead to an infinite loop, + // but the filesystem type can + entry.put("filesystem", fsType); - boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException, NoModificationAllowedException; + return entry; - JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException; + } - JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException; + public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Boolean isDir) throws JSONException { + return makeEntryForPath(inputURL.fullPath, inputURL.filesystemType, isDir); + } - JSONObject getParentForLocalURL(LocalFilesystemURL inputURL) throws IOException; + abstract JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException; - JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName, - Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException; + abstract JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, String fileName, + JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException; - void readFileAtURL(LocalFilesystemURL inputURL, int start, int end, + abstract boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException; + + abstract boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException, NoModificationAllowedException; + + abstract JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException; + + abstract JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException; + + public JSONObject getParentForLocalURL(LocalFilesystemURL inputURL) throws IOException { + LocalFilesystemURL newURL = new LocalFilesystemURL(inputURL.URL); + + if (!("".equals(inputURL.fullPath) || "/".equals(inputURL.fullPath))) { + int end = inputURL.fullPath.endsWith("/") ? 1 : 0; + int lastPathStartsAt = inputURL.fullPath.lastIndexOf('/', inputURL.fullPath.length()-end)+1; + newURL.fullPath = newURL.fullPath.substring(0,lastPathStartsAt); + } + return getEntryForLocalURL(newURL); + } + + protected LocalFilesystemURL makeDestinationURL(String newName, LocalFilesystemURL srcURL, LocalFilesystemURL destURL) { + // I know this looks weird but it is to work around a JSON bug. + if ("null".equals(newName) || "".equals(newName)) { + newName = srcURL.URL.getLastPathSegment();; + } + + String newDest = destURL.URL.toString(); + if (newDest.endsWith("/")) { + newDest = newDest + newName; + } else { + newDest = newDest + "/" + newName; + } + return new LocalFilesystemURL(newDest); + } + + /* Read a source URL (possibly from a different filesystem, srcFs,) and copy it to + * the destination URL on this filesystem, optionally with a new filename. + * If move is true, then this method should either perform an atomic move operation + * or remove the source file when finished. + */ + JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName, + Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException { + // This is "the hard way" -- transfer data between arbitrary filesystem urls/ + // Gets an input stream from src, and writes its contents to an output stream + // from dest. + + // First, check to see that we can do it + if (!move || srcFs.canRemoveFileAtLocalURL(srcURL)) { + final LocalFilesystemURL destination = makeDestinationURL(newName, srcURL, destURL); + srcFs.readFileAtURL(srcURL, 0, -1, new ReadFileCallback() { + public void handleData(InputStream inputStream, String contentType) throws IOException { + if (inputStream != null) { + //write data to file + OutputStream os = getOutputStreamForURL(destination); + final int BUFFER_SIZE = 8192; + byte[] buffer = new byte[BUFFER_SIZE]; + + for (;;) { + int bytesRead = inputStream.read(buffer, 0, BUFFER_SIZE); + + if (bytesRead <= 0) { + break; + } + os.write(buffer, 0, bytesRead); + } + os.close(); + } else { + throw new IOException("Cannot read file at source URL"); + } + } + }); + if (move) { + // Delete original + srcFs.removeFileAtLocalURL(srcURL); + } + return makeEntryForURL(destination, false); + } else { + throw new NoModificationAllowedException("Cannot move file at source URL"); + } + } + + abstract OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws IOException; + + abstract void readFileAtURL(LocalFilesystemURL inputURL, long start, long end, ReadFileCallback readFileCallback) throws IOException; - long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, + abstract long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, boolean isBinary) throws NoModificationAllowedException, IOException; - long truncateFileAtURL(LocalFilesystemURL inputURL, long size) + abstract long truncateFileAtURL(LocalFilesystemURL inputURL, long size) throws IOException, NoModificationAllowedException; // This method should return null if filesystem urls cannot be mapped to paths - String filesystemPathForURL(LocalFilesystemURL url); - - LocalFilesystemURL URLforFilesystemPath(String path); - - boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL); + abstract String filesystemPathForURL(LocalFilesystemURL url); + + abstract LocalFilesystemURL URLforFilesystemPath(String path); + + abstract boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL); + + protected class LimitedInputStream extends FilterInputStream { + long numBytesToRead; + public LimitedInputStream(InputStream in, long numBytesToRead) { + super(in); + this.numBytesToRead = numBytesToRead; + } + @Override + public int read() throws IOException { + if (numBytesToRead <= 0) { + return -1; + } + numBytesToRead--; + return in.read(); + } + @Override + public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException { + if (numBytesToRead <= 0) { + return -1; + } + int bytesToRead = byteCount; + if (byteCount > numBytesToRead) { + bytesToRead = (int)numBytesToRead; // Cast okay; long is less than int here. + } + int numBytesRead = in.read(buffer, byteOffset, bytesToRead); + numBytesToRead -= numBytesRead; + return numBytesRead; + } + } } http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/eaeb5981/src/android/LocalFilesystem.java ---------------------------------------------------------------------- diff --git a/src/android/LocalFilesystem.java b/src/android/LocalFilesystem.java index 6c16e45..f0d919f 100644 --- a/src/android/LocalFilesystem.java +++ b/src/android/LocalFilesystem.java @@ -7,6 +7,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; @@ -18,7 +19,7 @@ import org.json.JSONObject; import android.util.Base64; import android.net.Uri; -public class LocalFilesystem implements Filesystem { +public class LocalFilesystem extends Filesystem { private String name; private String fsRoot; @@ -55,24 +56,6 @@ public class LocalFilesystem implements Filesystem { return null; } - public static JSONObject makeEntryForPath(String path, int fsType, Boolean isDir) throws JSONException { - JSONObject entry = new JSONObject(); - - int end = path.endsWith("/") ? 1 : 0; - String[] parts = path.substring(0,path.length()-end).split("/"); - String name = parts[parts.length-1]; - entry.put("isFile", !isDir); - entry.put("isDirectory", isDir); - entry.put("name", name); - entry.put("fullPath", path); - // The file system can't be specified, as it would lead to an infinite loop, - // but the filesystem type can - entry.put("filesystem", fsType); - - return entry; - - } - public JSONObject makeEntryForFile(File file, int fsType) throws JSONException { String path = this.fullPathForFilesystemPath(file.getAbsolutePath()); if (path != null) { @@ -240,18 +223,6 @@ public class LocalFilesystem implements Filesystem { return metadata; } - @Override - public JSONObject getParentForLocalURL(LocalFilesystemURL inputURL) throws IOException { - LocalFilesystemURL newURL = new LocalFilesystemURL(inputURL.URL); - - if (!("".equals(inputURL.fullPath) || "/".equals(inputURL.fullPath))) { - int end = inputURL.fullPath.endsWith("/") ? 1 : 0; - int lastPathStartsAt = inputURL.fullPath.lastIndexOf('/', inputURL.fullPath.length()-end)+1; - newURL.fullPath = newURL.fullPath.substring(0,lastPathStartsAt); - } - return getEntryForLocalURL(newURL); - } - /** * Check to see if the user attempted to copy an entry into its parent without changing its name, * or attempted to copy a directory into a directory that it contains directly or indirectly. @@ -272,31 +243,6 @@ public class LocalFilesystem implements Filesystem { return false; } - - /** - * Creates the destination File object based on name passed in - * - * @param newName for the file directory to be called, if null use existing file name - * @param fp represents the source file - * @param destination represents the destination file - * @return a File object that represents the destination - */ - private File createDestination(String newName, String oldName, File destination) { - File destFile = null; - - // I know this looks weird but it is to work around a JSON bug. - if ("null".equals(newName) || "".equals(newName)) { - newName = null; - } - - if (newName != null) { - destFile = new File(destination.getAbsolutePath() + File.separator + newName); - } else { - destFile = new File(destination.getAbsolutePath() + File.separator + oldName); - } - return destFile; - } - /** * Copy a file * @@ -473,92 +419,71 @@ public class LocalFilesystem implements Filesystem { throw new FileNotFoundException("The source does not exist"); } - // Figure out where we should be copying to - String originalName = srcURL.URL.getLastPathSegment(); - final File destination = createDestination(newName, originalName, destinationDir); - if (LocalFilesystem.class.isInstance(srcFs)) { /* Same FS, we can shortcut with NSFileManager operations */ + + // Figure out where we should be copying to + final LocalFilesystemURL destinationURL = makeDestinationURL(newName, srcURL, destURL); + String srcFilesystemPath = this.filesystemPathForURL(srcURL); - File source = new File(srcFilesystemPath); + File sourceFile = new File(srcFilesystemPath); + String destFilesystemPath = this.filesystemPathForURL(destinationURL); + File destinationFile = new File(destFilesystemPath); - if (!source.exists()) { + if (!sourceFile.exists()) { // The file/directory we are copying doesn't exist so we should fail. throw new FileNotFoundException("The source does not exist"); } // Check to see if source and destination are the same file - if (source.getAbsolutePath().equals(destination.getAbsolutePath())) { + if (sourceFile.getAbsolutePath().equals(destinationFile.getAbsolutePath())) { throw new InvalidModificationException("Can't copy a file onto itself"); } - if (source.isDirectory()) { + if (sourceFile.isDirectory()) { if (move) { - return moveDirectory(source, destination, destURL.filesystemType); + return moveDirectory(sourceFile, destinationFile, destURL.filesystemType); } else { - return copyDirectory(source, destination, destURL.filesystemType); + return copyDirectory(sourceFile, destinationFile, destURL.filesystemType); } } else { if (move) { - return moveFile(source, destination, destURL.filesystemType); + return moveFile(sourceFile, destinationFile, destURL.filesystemType); } else { - return copyFile(source, destination, destURL.filesystemType); + return copyFile(sourceFile, destinationFile, destURL.filesystemType); } } } else { // Need to copy the hard way - // First, check to see that we can do it - if (!move || srcFs.canRemoveFileAtLocalURL(srcURL)) { - srcFs.readFileAtURL(srcURL, 0, -1, new ReadFileCallback() { - public void handleData(byte[] data, String contentType) throws IOException { - if (data != null) { - //write data to file - FileOutputStream os = new FileOutputStream(destination); - os.write(data); - os.close(); - } else { - throw new IOException("Cannot read file at source URL"); - } - } - }); - if (move) { - // Delete original - srcFs.removeFileAtLocalURL(srcURL); - } - return makeEntryForFile(destination, destURL.filesystemType); - } else { - throw new NoModificationAllowedException("Cannot move file at source URL"); - } + return super.copyFileToURL(destURL, newName, srcFs, srcURL, move); } } @Override - public void readFileAtURL(LocalFilesystemURL inputURL, int start, int end, + public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end, ReadFileCallback readFileCallback) throws IOException { - int numBytesToRead = end - start; - byte[] bytes = new byte[numBytesToRead]; - String contentType; - File file = new File(this.filesystemPathForURL(inputURL)); - contentType = FileHelper.getMimeTypeForExtension(file.getAbsolutePath()); + String contentType = FileHelper.getMimeTypeForExtension(file.getAbsolutePath()); - InputStream inputStream = new FileInputStream(file); - int numBytesRead = 0; + if (end < 0) { + end = file.length(); + } + long numBytesToRead = end - start; + + InputStream rawInputStream = new FileInputStream(file); try { if (start > 0) { - inputStream.skip(start); - } - while (numBytesToRead > 0 && (numBytesRead = inputStream.read(bytes, numBytesRead, numBytesToRead)) >= 0) { - numBytesToRead -= numBytesRead; + rawInputStream.skip(start); } + LimitedInputStream inputStream = new LimitedInputStream(rawInputStream, numBytesToRead); + readFileCallback.handleData(inputStream, contentType); } finally { - inputStream.close(); + rawInputStream.close(); } - readFileCallback.handleData(bytes, contentType); } - + @Override public long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, boolean isBinary) throws IOException, NoModificationAllowedException { @@ -630,4 +555,12 @@ public class LocalFilesystem implements Filesystem { return file.exists(); } + @Override + OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws FileNotFoundException { + String path = filesystemPathForURL(inputURL); + File file = new File(path); + FileOutputStream os = new FileOutputStream(file); + return os; + } + } http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/eaeb5981/src/android/LocalFilesystemURL.java ---------------------------------------------------------------------- diff --git a/src/android/LocalFilesystemURL.java b/src/android/LocalFilesystemURL.java index 0dc547e..1065382 100644 --- a/src/android/LocalFilesystemURL.java +++ b/src/android/LocalFilesystemURL.java @@ -24,8 +24,8 @@ public class LocalFilesystemURL { if (fsType == FileUtils.PERSISTENT) { return URL.getPath().substring(11); } - if (fsType == FileUtils.APPLICATION) { - return URL.getPath(); + if (fsType == FileUtils.CONTENT) { + return '/' + URL.getHost() + URL.getPath(); } return null; } @@ -41,7 +41,7 @@ public class LocalFilesystemURL { } } } else if ("content".equals(URL.getScheme())) { - return FileUtils.APPLICATION; + return FileUtils.CONTENT; } return -1; } @@ -50,5 +50,4 @@ public class LocalFilesystemURL { this(Uri.parse(strURL)); } - } http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/eaeb5981/src/android/ReadFileCallback.java ---------------------------------------------------------------------- diff --git a/src/android/ReadFileCallback.java b/src/android/ReadFileCallback.java deleted file mode 100644 index b8ba02d..0000000 --- a/src/android/ReadFileCallback.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.apache.cordova.file; - -import java.io.IOException; - -public interface ReadFileCallback { - public void handleData(byte[] data, String contentType) throws IOException; -}