Java tutorial
/** * Copyright 2015 Kyle J Brock * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.nuclei.splice.internal; import android.content.Context; import android.net.Uri; import android.os.Environment; import android.support.annotation.VisibleForTesting; import android.support.v4.util.ArrayMap; import android.support.v4.util.AtomicFile; 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.UUID; import java.util.concurrent.locks.ReentrantLock; import io.nuclei.splice.service.Source; public class FileManager { public static final int TYPE_CACHE_FILE = 1; public static final int TYPE_LOCAL_FILE = 2; public static final int TYPE_EXTERNAL_FILE = 3; static final ArrayMap<FileRef, ReentrantLock> sLocks = new ArrayMap<>(); public static ReentrantLock lock(FileRef ref) { ReentrantLock lock; boolean locked; synchronized (sLocks) { lock = sLocks.get(ref); if (lock == null) { lock = new ReentrantLock(); sLocks.put(ref, lock); } locked = lock.tryLock(); } if (!locked) { while (true) { synchronized (sLocks) { try { sLocks.wait(); } catch (InterruptedException ignore) { } if (lock.tryLock()) { ReentrantLock newLock = sLocks.get(ref); if (newLock == null) { sLocks.put(ref, lock); break; } else if (newLock != lock) { lock.unlock(); lock = newLock; if (lock.tryLock()) { break; } } else { break; } } } } } return lock; } public static void unlock(FileRef ref) { synchronized (sLocks) { ReentrantLock lock = sLocks.get(ref); sLocks.remove(ref); lock.unlock(); sLocks.notifyAll(); } } static final String DIR = ".splice"; private static File getBaseFile(Context context, int type) throws IOException { File base = null; if (type == TYPE_EXTERNAL_FILE && Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) base = new File(context.getExternalFilesDir(null), DIR); else if (type == TYPE_CACHE_FILE) base = new File(context.getCacheDir(), DIR); else if (type == TYPE_LOCAL_FILE) base = new File(context.getFilesDir(), DIR); if (base == null) throw new IOException("Couldn't access directory type: " + type); if (!base.exists() && !base.mkdirs()) throw new IOException("Couldn't create directory: " + base); return base; } @VisibleForTesting public static AtomicFile getAtomicFile(Context context, FileRef ref) throws IOException { File base = getBaseFile(context, ref.type); if (base == null) throw new IOException("Unable to read/write to type: " + ref.type); return new AtomicFile(new File(base, ref.id)); } @VisibleForTesting public static File getFile(Context context, FileRef ref) throws IOException { File base = getBaseFile(context, ref.type); if (base == null) throw new IOException("Unable to read/write to type: " + ref.type); return new File(base, ref.id); } public static Uri newUri(Context context, String id) throws IOException { FileRef ref = newRef(id); return newUri(context, ref); } public static Uri newUri(Context context, FileRef ref) throws IOException { File base = getBaseFile(context, ref.type); if (base == null) throw new IOException("Unable to read/write to type: " + ref.type); return Uri.fromFile(new File(base, ref.id)); } public static FileRef newRef(int type) { String id = UUID.randomUUID().toString(); return new FileRef(id, type); } public static FileRef newRef(String id) { return new FileRef(id); } public static InputStream open(Context context, FileRef ref) throws IOException { return getAtomicFile(context, ref).openRead(); } public static void delete(Context context, FileRef ref) throws IOException { getAtomicFile(context, ref).delete(); } public static void copy(Context context, FileRef ref, OutputStream out) throws IOException { AtomicFile file = getAtomicFile(context, ref); FileInputStream in = file.openRead(); try { copy(in, out); } finally { in.close(); } } public static void copy(Context context, FileRef ref, InputStream in) throws IOException { AtomicFile file = getAtomicFile(context, ref); FileOutputStream out = file.startWrite(); try { copy(in, out); } finally { file.finishWrite(out); } } public static void copy(Context context, DbRef ref, Source source) throws IOException { AtomicFile file = getAtomicFile(context, ref.ref); FileOutputStream out = file.startWrite(); try { source.process(context, ref, out); } finally { file.finishWrite(out); } } public static void copy(InputStream in, OutputStream out) throws IOException { byte[] buf = ByteArrayPool.get().getBuf(4096); try { int n; while (-1 != (n = in.read(buf))) { out.write(buf, 0, n); } } finally { ByteArrayPool.get().returnBuf(buf); } } }