arnaucube
arnaucube/openEventsPlatformAppPublic Self-hosted repository
openEventsPlatformApp/plugins/cordova-plugin-file/src/android/AssetFilesystem.java
plugins/cordova-plugin-file/src/android/AssetFilesystem.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.content.res.AssetManager;
 22import android.net.Uri;
 23
 24import org.apache.cordova.CordovaResourceApi;
 25import org.apache.cordova.LOG;
 26import org.json.JSONArray;
 27import org.json.JSONException;
 28import org.json.JSONObject;
 29
 30import java.io.File;
 31import java.io.FileNotFoundException;
 32import java.io.IOException;
 33import java.io.ObjectInputStream;
 34import java.util.HashMap;
 35import java.util.Map;
 36
 37public class AssetFilesystem extends Filesystem {
 38
 39    private final AssetManager assetManager;
 40
 41    // A custom gradle hook creates the cdvasset.manifest file, which speeds up asset listing a tonne.
 42    // See: http://stackoverflow.com/questions/16911558/android-assetmanager-list-incredibly-slow
 43    private static Object listCacheLock = new Object();
 44    private static boolean listCacheFromFile;
 45    private static Map<String, String[]> listCache;
 46    private static Map<String, Long> lengthCache;
 47
 48    private static final String LOG_TAG = "AssetFilesystem";
 49
 50    private void lazyInitCaches() {
 51        synchronized (listCacheLock) {
 52            if (listCache == null) {
 53                ObjectInputStream ois = null;
 54                try {
 55                    ois = new ObjectInputStream(assetManager.open("cdvasset.manifest"));
 56                    listCache = (Map<String, String[]>) ois.readObject();
 57                    lengthCache = (Map<String, Long>) ois.readObject();
 58                    listCacheFromFile = true;
 59                } catch (ClassNotFoundException e) {
 60                    e.printStackTrace();
 61                } catch (IOException e) {
 62                    // Asset manifest won't exist if the gradle hook isn't set up correctly.
 63                } finally {
 64                    if (ois != null) {
 65                        try {
 66                            ois.close();
 67                        } catch (IOException e) {
 68                            LOG.d(LOG_TAG, e.getLocalizedMessage());
 69                        }
 70                    }
 71                }
 72                if (listCache == null) {
 73                    LOG.w("AssetFilesystem", "Asset manifest not found. Recursive copies and directory listing will be slow.");
 74                    listCache = new HashMap<String, String[]>();
 75                }
 76            }
 77        }
 78    }
 79
 80    private String[] listAssets(String assetPath) throws IOException {
 81        if (assetPath.startsWith("/")) {
 82            assetPath = assetPath.substring(1);
 83        }
 84        if (assetPath.endsWith("/")) {
 85            assetPath = assetPath.substring(0, assetPath.length() - 1);
 86        }
 87        lazyInitCaches();
 88        String[] ret = listCache.get(assetPath);
 89        if (ret == null) {
 90            if (listCacheFromFile) {
 91                ret = new String[0];
 92            } else {
 93                ret = assetManager.list(assetPath);
 94                listCache.put(assetPath, ret);
 95            }
 96        }
 97        return ret;
 98    }
 99
100    private long getAssetSize(String assetPath) throws FileNotFoundException {
101        if (assetPath.startsWith("/")) {
102            assetPath = assetPath.substring(1);
103        }
104        lazyInitCaches();
105        if (lengthCache != null) {
106            Long ret = lengthCache.get(assetPath);
107            if (ret == null) {
108                throw new FileNotFoundException("Asset not found: " + assetPath);
109            }
110            return ret;
111        }
112        CordovaResourceApi.OpenForReadResult offr = null;
113        try {
114            offr = resourceApi.openForRead(nativeUriForFullPath(assetPath));
115            long length = offr.length;
116            if (length < 0) {
117                // available() doesn't always yield the file size, but for assets it does.
118                length = offr.inputStream.available();
119            }
120            return length;
121        } catch (IOException e) {
122            FileNotFoundException fnfe = new FileNotFoundException("File not found: " + assetPath);
123            fnfe.initCause(e);
124            throw fnfe;
125        } finally {
126            if (offr != null) {
127                try {
128                    offr.inputStream.close();
129                } catch (IOException e) {
130                    LOG.d(LOG_TAG, e.getLocalizedMessage());
131                }
132            }
133        }
134    }
135
136    public AssetFilesystem(AssetManager assetManager, CordovaResourceApi resourceApi) {
137        super(Uri.parse("file:///android_asset/"), "assets", resourceApi);
138        this.assetManager = assetManager;
139	}
140
141    @Override
142    public Uri toNativeUri(LocalFilesystemURL inputURL) {
143        return nativeUriForFullPath(inputURL.path);
144    }
145
146    @Override
147    public LocalFilesystemURL toLocalUri(Uri inputURL) {
148        if (!"file".equals(inputURL.getScheme())) {
149            return null;
150        }
151        File f = new File(inputURL.getPath());
152        // Removes and duplicate /s (e.g. file:///a//b/c)
153        Uri resolvedUri = Uri.fromFile(f);
154        String rootUriNoTrailingSlash = rootUri.getEncodedPath();
155        rootUriNoTrailingSlash = rootUriNoTrailingSlash.substring(0, rootUriNoTrailingSlash.length() - 1);
156        if (!resolvedUri.getEncodedPath().startsWith(rootUriNoTrailingSlash)) {
157            return null;
158        }
159        String subPath = resolvedUri.getEncodedPath().substring(rootUriNoTrailingSlash.length());
160        // Strip leading slash
161        if (!subPath.isEmpty()) {
162            subPath = subPath.substring(1);
163        }
164        Uri.Builder b = new Uri.Builder()
165            .scheme(LocalFilesystemURL.FILESYSTEM_PROTOCOL)
166            .authority("localhost")
167            .path(name);
168        if (!subPath.isEmpty()) {
169            b.appendEncodedPath(subPath);
170        }
171        if (isDirectory(subPath) || inputURL.getPath().endsWith("/")) {
172            // Add trailing / for directories.
173            b.appendEncodedPath("");
174        }
175        return LocalFilesystemURL.parse(b.build());
176    }
177
178    private boolean isDirectory(String assetPath) {
179        try {
180            return listAssets(assetPath).length != 0;
181        } catch (IOException e) {
182            return false;
183        }
184    }
185
186    @Override
187    public LocalFilesystemURL[] listChildren(LocalFilesystemURL inputURL) throws FileNotFoundException {
188        String pathNoSlashes = inputURL.path.substring(1);
189        if (pathNoSlashes.endsWith("/")) {
190            pathNoSlashes = pathNoSlashes.substring(0, pathNoSlashes.length() - 1);
191        }
192
193        String[] files;
194        try {
195            files = listAssets(pathNoSlashes);
196        } catch (IOException e) {
197            FileNotFoundException fnfe = new FileNotFoundException();
198            fnfe.initCause(e);
199            throw fnfe;
200        }
201
202        LocalFilesystemURL[] entries = new LocalFilesystemURL[files.length];
203        for (int i = 0; i < files.length; ++i) {
204            entries[i] = localUrlforFullPath(new File(inputURL.path, files[i]).getPath());
205        }
206        return entries;
207	}
208
209    @Override
210    public JSONObject getFileForLocalURL(LocalFilesystemURL inputURL,
211                                         String path, JSONObject options, boolean directory)
212            throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
213        if (options != null && options.optBoolean("create")) {
214            throw new UnsupportedOperationException("Assets are read-only");
215        }
216
217        // Check whether the supplied path is absolute or relative
218        if (directory && !path.endsWith("/")) {
219            path += "/";
220        }
221
222        LocalFilesystemURL requestedURL;
223        if (path.startsWith("/")) {
224            requestedURL = localUrlforFullPath(normalizePath(path));
225        } else {
226            requestedURL = localUrlforFullPath(normalizePath(inputURL.path + "/" + path));
227        }
228
229        // Throws a FileNotFoundException if it doesn't exist.
230        getFileMetadataForLocalURL(requestedURL);
231
232        boolean isDir = isDirectory(requestedURL.path);
233        if (directory && !isDir) {
234            throw new TypeMismatchException("path doesn't exist or is file");
235        } else if (!directory && isDir) {
236            throw new TypeMismatchException("path doesn't exist or is directory");
237        }
238
239        // Return the directory
240        return makeEntryForURL(requestedURL);
241    }
242
243    @Override
244	public JSONObject getFileMetadataForLocalURL(LocalFilesystemURL inputURL) throws FileNotFoundException {
245        JSONObject metadata = new JSONObject();
246        long size = inputURL.isDirectory ? 0 : getAssetSize(inputURL.path);
247        try {
248        	metadata.put("size", size);
249        	metadata.put("type", inputURL.isDirectory ? "text/directory" : resourceApi.getMimeType(toNativeUri(inputURL)));
250        	metadata.put("name", new File(inputURL.path).getName());
251        	metadata.put("fullPath", inputURL.path);
252        	metadata.put("lastModifiedDate", 0);
253        } catch (JSONException e) {
254            return null;
255        }
256        return metadata;
257	}
258
259	@Override
260	public boolean canRemoveFileAtLocalURL(LocalFilesystemURL inputURL) {
261		return false;
262	}
263
264    @Override
265    long writeToFileAtURL(LocalFilesystemURL inputURL, String data, int offset, boolean isBinary) throws NoModificationAllowedException, IOException {
266        throw new NoModificationAllowedException("Assets are read-only");
267    }
268
269    @Override
270    long truncateFileAtURL(LocalFilesystemURL inputURL, long size) throws IOException, NoModificationAllowedException {
271        throw new NoModificationAllowedException("Assets are read-only");
272    }
273
274    @Override
275    String filesystemPathForURL(LocalFilesystemURL url) {
276        return new File(rootUri.getPath(), url.path).toString();
277    }
278
279    @Override
280    LocalFilesystemURL URLforFilesystemPath(String path) {
281        return null;
282    }
283
284    @Override
285    boolean removeFileAtLocalURL(LocalFilesystemURL inputURL) throws InvalidModificationException, NoModificationAllowedException {
286        throw new NoModificationAllowedException("Assets are read-only");
287    }
288
289    @Override
290    boolean recursiveRemoveFileAtLocalURL(LocalFilesystemURL inputURL) throws NoModificationAllowedException {
291        throw new NoModificationAllowedException("Assets are read-only");
292    }
293
294}