arnaucube
arnaucube/openEventsPlatformAppPublic Self-hosted repository
openEventsPlatformApp/plugins/cordova-plugin-file/src/android/Filesystem.java
plugins/cordova-plugin-file/src/android/Filesystem.javaRaw
  1/*
  2       Licensed to the Apache Software Foundation (ASF) under one
  3       or more contributor license agreements.  See the NOTICE file
  4       distributed with this work for additional information
  5       regarding copyright ownership.  The ASF licenses this file
  6       to you under the Apache License, Version 2.0 (the
  7       "License"); you may not use this file except in compliance
  8       with the License.  You may obtain a copy of the License at
  9
 10         http://www.apache.org/licenses/LICENSE-2.0
 11
 12       Unless required by applicable law or agreed to in writing,
 13       software distributed under the License is distributed on an
 14       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15       KIND, either express or implied.  See the License for the
 16       specific language governing permissions and limitations
 17       under the License.
 18 */
 19package org.apache.cordova.file;
 20
 21import android.net.Uri;
 22
 23import java.io.File;
 24import java.io.FileNotFoundException;
 25import java.io.FilterInputStream;
 26import java.io.IOException;
 27import java.io.InputStream;
 28import java.io.OutputStream;
 29import java.util.ArrayList;
 30import java.util.Arrays;
 31
 32import org.apache.cordova.CordovaResourceApi;
 33import org.json.JSONArray;
 34import org.json.JSONException;
 35import org.json.JSONObject;
 36
 37public abstract class Filesystem {
 38
 39    protected final Uri rootUri;
 40    protected final CordovaResourceApi resourceApi;
 41    public final String name;
 42    private JSONObject rootEntry;
 43
 44    public Filesystem(Uri rootUri, String name, CordovaResourceApi resourceApi) {
 45        this.rootUri = rootUri;
 46        this.name = name;
 47        this.resourceApi = resourceApi;
 48    }
 49
 50    public interface ReadFileCallback {
 51		public void handleData(InputStream inputStream, String contentType) throws IOException;
 52	}
 53
 54    public static JSONObject makeEntryForURL(LocalFilesystemURL inputURL, Uri nativeURL) {
 55        try {
 56            String path = inputURL.path;
 57            int end = path.endsWith("/") ? 1 : 0;
 58            String[] parts = path.substring(0, path.length() - end).split("/+");
 59            String fileName = parts[parts.length - 1];
 60
 61            JSONObject entry = new JSONObject();
 62            entry.put("isFile", !inputURL.isDirectory);
 63            entry.put("isDirectory", inputURL.isDirectory);
 64            entry.put("name", fileName);
 65            entry.put("fullPath", path);
 66            // The file system can't be specified, as it would lead to an infinite loop,
 67            // but the filesystem name can be.
 68            entry.put("filesystemName", inputURL.fsName);
 69            // Backwards compatibility
 70            entry.put("filesystem", "temporary".equals(inputURL.fsName) ? 0 : 1);
 71
 72            String nativeUrlStr = nativeURL.toString();
 73            if (inputURL.isDirectory && !nativeUrlStr.endsWith("/")) {
 74                nativeUrlStr += "/";
 75            }
 76            entry.put("nativeURL", nativeUrlStr);
 77            return entry;
 78        } catch (JSONException e) {
 79            e.printStackTrace();
 80            throw new RuntimeException(e);
 81        }
 82    }
 83
 84    public JSONObject makeEntryForURL(LocalFilesystemURL inputURL) {
 85        Uri nativeUri = toNativeUri(inputURL);
 86        return nativeUri == null ? null : makeEntryForURL(inputURL, nativeUri);
 87    }
 88
 89    public JSONObject makeEntryForNativeUri(Uri nativeUri) {
 90        LocalFilesystemURL inputUrl = toLocalUri(nativeUri);
 91        return inputUrl == null ? null : makeEntryForURL(inputUrl, nativeUri);
 92    }
 93
 94    public JSONObject getEntryForLocalURL(LocalFilesystemURL inputURL) throws IOException {
 95        return makeEntryForURL(inputURL);
 96    }
 97
 98    public JSONObject makeEntryForFile(File file) {
 99        return makeEntryForNativeUri(Uri.fromFile(file));
100    }
101
102    abstract JSONObject getFileForLocalURL(LocalFilesystemURL inputURL, String path,
103			JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException;
104
105	abstract boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException;
106
107	abstract boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws FileExistsException, NoModificationAllowedException;
108
109	abstract LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException;
110
111    public final JSONArray readEntriesAtLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
112        LocalFilesystemURL[] children = listChildren(inputURL);
113        JSONArray entries = new JSONArray();
114        if (children != null) {
115            for (LocalFilesystemURL url : children) {
116                entries.put(makeEntryForURL(url));
117            }
118        }
119        return entries;
120    }
121
122	abstract JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException;
123
124    public Uri getRootUri() {
125        return rootUri;
126    }
127
128    public boolean exists(LocalFilesystemURL inputURL) {
129        try {
130            getFileMetadataForLocalURL(inputURL);
131        } catch (FileNotFoundException e) {
132            return false;
133        }
134        return true;
135    }
136
137    public Uri nativeUriForFullPath(String fullPath) {
138        Uri ret = null;
139        if (fullPath != null) {
140            String encodedPath = Uri.fromFile(new File(fullPath)).getEncodedPath();
141            if (encodedPath.startsWith("/")) {
142                encodedPath = encodedPath.substring(1);
143            }
144            ret = rootUri.buildUpon().appendEncodedPath(encodedPath).build();
145        }
146        return ret;
147    }
148
149    public LocalFilesystemURL localUrlforFullPath(String fullPath) {
150        Uri nativeUri = nativeUriForFullPath(fullPath);
151        if (nativeUri != null) {
152            return toLocalUri(nativeUri);
153        }
154        return null;
155    }
156
157    /**
158     * Removes multiple repeated //s, and collapses processes ../s.
159     */
160    protected static String normalizePath(String rawPath) {
161        // If this is an absolute path, trim the leading "/" and replace it later
162        boolean isAbsolutePath = rawPath.startsWith("/");
163        if (isAbsolutePath) {
164            rawPath = rawPath.replaceFirst("/+", "");
165        }
166        ArrayList<String> components = new ArrayList<String>(Arrays.asList(rawPath.split("/+")));
167        for (int index = 0; index < components.size(); ++index) {
168            if (components.get(index).equals("..")) {
169                components.remove(index);
170                if (index > 0) {
171                    components.remove(index-1);
172                    --index;
173                }
174            }
175        }
176        StringBuilder normalizedPath = new StringBuilder();
177        for(String component: components) {
178            normalizedPath.append("/");
179            normalizedPath.append(component);
180        }
181        if (isAbsolutePath) {
182            return normalizedPath.toString();
183        } else {
184            return normalizedPath.toString().substring(1);
185        }
186    }
187
188    /**
189     * Gets the free space in bytes available on this filesystem.
190     * Subclasses may override this method to return nonzero free space.
191     */
192    public long getFreeSpaceInBytes() {
193        return 0;
194    }
195
196    public abstract Uri toNativeUri(LocalFilesystemURL inputURL);
197    public abstract LocalFilesystemURL toLocalUri(Uri inputURL);
198
199    public JSONObject getRootEntry() {
200        if (rootEntry == null) {
201            rootEntry = makeEntryForNativeUri(rootUri);
202        }
203        return rootEntry;
204    }
205
206	public JSONObject getParentForLocalURL(LocalFilesystemURL inputURL) throws IOException {
207        Uri parentUri = inputURL.uri;
208        String parentPath = new File(inputURL.uri.getPath()).getParent();
209        if (!"/".equals(parentPath)) {
210            parentUri = inputURL.uri.buildUpon().path(parentPath + '/').build();
211		}
212		return getEntryForLocalURL(LocalFilesystemURL.parse(parentUri));
213	}
214
215    protected LocalFilesystemURL makeDestinationURL(String newName, LocalFilesystemURL srcURL, LocalFilesystemURL destURL, boolean isDirectory) {
216        // I know this looks weird but it is to work around a JSON bug.
217        if ("null".equals(newName) || "".equals(newName)) {
218            newName = srcURL.uri.getLastPathSegment();;
219        }
220
221        String newDest = destURL.uri.toString();
222        if (newDest.endsWith("/")) {
223            newDest = newDest + newName;
224        } else {
225            newDest = newDest + "/" + newName;
226        }
227        if (isDirectory) {
228            newDest += '/';
229        }
230        return LocalFilesystemURL.parse(newDest);
231    }
232
233	/* Read a source URL (possibly from a different filesystem, srcFs,) and copy it to
234	 * the destination URL on this filesystem, optionally with a new filename.
235	 * If move is true, then this method should either perform an atomic move operation
236	 * or remove the source file when finished.
237	 */
238    public JSONObject copyFileToURL(LocalFilesystemURL destURL, String newName,
239            Filesystem srcFs, LocalFilesystemURL srcURL, boolean move) throws IOException, InvalidModificationException, JSONException, NoModificationAllowedException, FileExistsException {
240        // First, check to see that we can do it
241        if (move && !srcFs.canRemoveFileAtLocalURL(srcURL)) {
242            throw new NoModificationAllowedException("Cannot move file at source URL");
243        }
244        final LocalFilesystemURL destination = makeDestinationURL(newName, srcURL, destURL, srcURL.isDirectory);
245
246        Uri srcNativeUri = srcFs.toNativeUri(srcURL);
247
248        CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(srcNativeUri);
249        OutputStream os = null;
250        try {
251            os = getOutputStreamForURL(destination);
252        } catch (IOException e) {
253            ofrr.inputStream.close();
254            throw e;
255        }
256        // Closes streams.
257        resourceApi.copyResource(ofrr, os);
258
259        if (move) {
260            srcFs.removeFileAtLocalURL(srcURL);
261        }
262        return getEntryForLocalURL(destination);
263    }
264
265    public OutputStream getOutputStreamForURL(LocalFilesystemURL inputURL) throws IOException {
266        return resourceApi.openOutputStream(toNativeUri(inputURL));
267    }
268
269    public void readFileAtURL(LocalFilesystemURL inputURL, long start, long end,
270                              ReadFileCallback readFileCallback) throws IOException {
271        CordovaResourceApi.OpenForReadResult ofrr = resourceApi.openForRead(toNativeUri(inputURL));
272        if (end < 0) {
273            end = ofrr.length;
274        }
275        long numBytesToRead = end - start;
276        try {
277            if (start > 0) {
278                ofrr.inputStream.skip(start);
279            }
280            InputStream inputStream = ofrr.inputStream;
281            if (end < ofrr.length) {
282                inputStream = new LimitedInputStream(inputStream, numBytesToRead);
283            }
284            readFileCallback.handleData(inputStream, ofrr.mimeType);
285        } finally {
286            ofrr.inputStream.close();
287        }
288    }
289
290	abstract long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset,
291			boolean isBinary) throws NoModificationAllowedException, IOException;
292
293	abstract long truncateFileAtURL(LocalFilesystemURL inputURL, long size)
294			throws IOException, NoModificationAllowedException;
295
296	// This method should return null if filesystem urls cannot be mapped to paths
297	abstract String filesystemPathForURL(LocalFilesystemURL url);
298
299	abstract LocalFilesystemURL URLforFilesystemPath(String path);
300
301	abstract boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL);
302
303    protected class LimitedInputStream extends FilterInputStream {
304        long numBytesToRead;
305        public LimitedInputStream(InputStream in, long numBytesToRead) {
306            super(in);
307            this.numBytesToRead = numBytesToRead;
308        }
309        @Override
310        public int read() throws IOException {
311            if (numBytesToRead <= 0) {
312                return -1;
313            }
314            numBytesToRead--;
315            return in.read();
316        }
317        @Override
318        public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
319            if (numBytesToRead <= 0) {
320                return -1;
321            }
322            int bytesToRead = byteCount;
323            if (byteCount > numBytesToRead) {
324                bytesToRead = (int)numBytesToRead; // Cast okay; long is less than int here.
325            }
326            int numBytesRead = in.read(buffer, byteOffset, bytesToRead);
327            numBytesToRead -= numBytesRead;
328            return numBytesRead;
329        }
330    }
331}