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