1a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller/* 2a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Copyright (C) 2015 Square, Inc. 3a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * 4a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Licensed under the Apache License, Version 2.0 (the "License"); 5a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * you may not use this file except in compliance with the License. 6a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * You may obtain a copy of the License at 7a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * 8a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * http://www.apache.org/licenses/LICENSE-2.0 9a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * 10a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Unless required by applicable law or agreed to in writing, software 11a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * distributed under the License is distributed on an "AS IS" BASIS, 12a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * See the License for the specific language governing permissions and 14a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * limitations under the License. 15a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 16a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerpackage com.squareup.okhttp.internal.io; 17a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 18a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.io.File; 19a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.io.FileNotFoundException; 20a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport java.io.IOException; 21a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport okio.Okio; 22a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport okio.Sink; 23a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport okio.Source; 24a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 25a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller/** 26a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Access to read and write files on a hierarchical data store. Most callers should use the {@link 27a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * #SYSTEM} implementation, which uses the host machine's local file system. Alternate 28a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * implementations may be used to inject faults (for testing) or to transform stored data (to add 29a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * encryption, for example). 30a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * 31a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * <p>All operations on a file system are racy. For example, guarding a call to {@link #source} 32a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * with {@link #exists} does not guarantee that {@link FileNotFoundException} will not be thrown. 33a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * The file may be moved between the two calls! 34a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * 35a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * <p>This interface is less ambitious than {@link java.nio.file.FileSystem} introduced in Java 7. 36a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * It lacks important features like file watching, metadata, permissions, and disk space 37a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * information. In exchange for these limitations, this interface is easier to implement and works 38a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * on all versions of Java and Android. 39a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 40a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerpublic interface FileSystem { 41a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** The host machine's local file system. */ 42a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller FileSystem SYSTEM = new FileSystem() { 43a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override public Source source(File file) throws FileNotFoundException { 44a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return Okio.source(file); 45a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 46a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 47a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override public Sink sink(File file) throws FileNotFoundException { 48a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller try { 49a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return Okio.sink(file); 50a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } catch (FileNotFoundException e) { 51a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // Maybe the parent directory doesn't exist? Try creating it first. 52a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller file.getParentFile().mkdirs(); 53a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return Okio.sink(file); 54a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 55a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 56a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 57a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override public Sink appendingSink(File file) throws FileNotFoundException { 58a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller try { 59a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return Okio.appendingSink(file); 60a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } catch (FileNotFoundException e) { 61a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // Maybe the parent directory doesn't exist? Try creating it first. 62a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller file.getParentFile().mkdirs(); 63a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return Okio.appendingSink(file); 64a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 65a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 66a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 67a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override public void delete(File file) throws IOException { 68a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller // If delete() fails, make sure it's because the file didn't exist! 69a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (!file.delete() && file.exists()) { 70a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller throw new IOException("failed to delete " + file); 71a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 72a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 73a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 74a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override public boolean exists(File file) throws IOException { 75a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return file.exists(); 76a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 77a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 78a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override public long size(File file) { 79a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller return file.length(); 80a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 81a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 82a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override public void rename(File from, File to) throws IOException { 83a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller delete(to); 84a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (!from.renameTo(to)) { 85a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller throw new IOException("failed to rename " + from + " to " + to); 86a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 87a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 88a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 89a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller @Override public void deleteContents(File directory) throws IOException { 90a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller File[] files = directory.listFiles(); 91a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (files == null) { 92a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller throw new IOException("not a readable directory: " + directory); 93a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 94a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller for (File file : files) { 95a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (file.isDirectory()) { 96a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller deleteContents(file); 97a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 98a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller if (!file.delete()) { 99a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller throw new IOException("failed to delete " + file); 100a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 101a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 102a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller } 103a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller }; 104a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 105a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** Reads from {@code file}. */ 106a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Source source(File file) throws FileNotFoundException; 107a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 108a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** 109a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Writes to {@code file}, discarding any data already present. Creates parent directories if 110a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * necessary. 111a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 112a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Sink sink(File file) throws FileNotFoundException; 113a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 114a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** 115a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Writes to {@code file}, appending if data is already present. Creates parent directories if 116a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * necessary. 117a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 118a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller Sink appendingSink(File file) throws FileNotFoundException; 119a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 120a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** Deletes {@code file} if it exists. Throws if the file exists and cannot be deleted. */ 121a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller void delete(File file) throws IOException; 122a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 123a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** Returns true if {@code file} exists on the file system. */ 124a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller boolean exists(File file) throws IOException; 125a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 126a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** Returns the number of bytes stored in {@code file}, or 0 if it does not exist. */ 127a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller long size(File file); 128a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 129a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** Renames {@code from} to {@code to}. Throws if the file cannot be renamed. */ 130a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller void rename(File from, File to) throws IOException; 131a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller 132a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller /** 133a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * Recursively delete the contents of {@code directory}. Throws an IOException if any file could 134a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller * not be deleted, or if {@code dir} is not a readable directory. 135a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller */ 136a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller void deleteContents(File directory) throws IOException; 137a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller} 138