1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.jimfs; 18 19 import static com.google.common.base.Preconditions.checkNotNull; 20 21 import com.google.common.annotations.VisibleForTesting; 22 import com.google.common.base.MoreObjects; 23 import com.google.common.collect.HashBasedTable; 24 import com.google.common.collect.ImmutableSet; 25 import com.google.common.collect.Table; 26 import java.io.IOException; 27 import java.util.concurrent.locks.ReadWriteLock; 28 import org.checkerframework.checker.nullness.compatqual.NullableDecl; 29 30 /** 31 * A file object, containing both the file's metadata and content. 32 * 33 * @author Colin Decker 34 */ 35 public abstract class File { 36 37 private final int id; 38 39 private int links; 40 41 private long creationTime; 42 private long lastAccessTime; 43 private long lastModifiedTime; 44 45 @NullableDecl // null when only the basic view is used (default) 46 private Table<String, String, Object> attributes; 47 File(int id)48 File(int id) { 49 this.id = id; 50 51 long now = System.currentTimeMillis(); // TODO(cgdecker): Use a Clock 52 this.creationTime = now; 53 this.lastAccessTime = now; 54 this.lastModifiedTime = now; 55 } 56 57 /** Returns the ID of this file. */ id()58 public int id() { 59 return id; 60 } 61 62 /** 63 * Returns the size, in bytes, of this file's content. Directories and symbolic links have a size 64 * of 0. 65 */ size()66 public long size() { 67 return 0; 68 } 69 70 /** Returns whether or not this file is a directory. */ isDirectory()71 public final boolean isDirectory() { 72 return this instanceof Directory; 73 } 74 75 /** Returns whether or not this file is a regular file. */ isRegularFile()76 public final boolean isRegularFile() { 77 return this instanceof RegularFile; 78 } 79 80 /** Returns whether or not this file is a symbolic link. */ isSymbolicLink()81 public final boolean isSymbolicLink() { 82 return this instanceof SymbolicLink; 83 } 84 85 /** 86 * Creates a new file of the same type as this file with the given ID. Does not copy the content 87 * of this file unless the cost of copying the content is minimal. This is because this method is 88 * called with a hold on the file system's lock. 89 */ copyWithoutContent(int id)90 abstract File copyWithoutContent(int id); 91 92 /** 93 * Copies the content of this file to the given file. The given file must be the same type of file 94 * as this file and should have no content. 95 * 96 * <p>This method is used for copying the content of a file after copying the file itself. Does 97 * nothing by default. 98 */ copyContentTo(File file)99 void copyContentTo(File file) throws IOException {} 100 101 /** 102 * Returns the read-write lock for this file's content, or {@code null} if there is no content 103 * lock. 104 */ 105 @NullableDecl contentLock()106 ReadWriteLock contentLock() { 107 return null; 108 } 109 110 /** Called when a stream or channel to this file is opened. */ opened()111 void opened() {} 112 113 /** 114 * Called when a stream or channel to this file is closed. If there are no more streams or 115 * channels open to the file and it has been deleted, its contents may be deleted. 116 */ closed()117 void closed() {} 118 119 /** 120 * Called when (a single link to) this file is deleted. There may be links remaining. Does nothing 121 * by default. 122 */ deleted()123 void deleted() {} 124 125 /** Returns whether or not this file is a root directory of the file system. */ isRootDirectory()126 final boolean isRootDirectory() { 127 // only root directories have their parent link pointing to themselves 128 return isDirectory() && equals(((Directory) this).parent()); 129 } 130 131 /** Returns the current count of links to this file. */ links()132 public final synchronized int links() { 133 return links; 134 } 135 136 /** 137 * Called when this file has been linked in a directory. The given entry is the new directory 138 * entry that links to this file. 139 */ linked(DirectoryEntry entry)140 void linked(DirectoryEntry entry) { 141 checkNotNull(entry); 142 } 143 144 /** Called when this file has been unlinked from a directory, either for a move or delete. */ unlinked()145 void unlinked() {} 146 147 /** Increments the link count for this file. */ incrementLinkCount()148 final synchronized void incrementLinkCount() { 149 links++; 150 } 151 152 /** Decrements the link count for this file. */ decrementLinkCount()153 final synchronized void decrementLinkCount() { 154 links--; 155 } 156 157 /** Gets the creation time of the file. */ 158 @SuppressWarnings("GoodTime") // should return a java.time.Instant getCreationTime()159 public final synchronized long getCreationTime() { 160 return creationTime; 161 } 162 163 /** Gets the last access time of the file. */ 164 @SuppressWarnings("GoodTime") // should return a java.time.Instant getLastAccessTime()165 public final synchronized long getLastAccessTime() { 166 return lastAccessTime; 167 } 168 169 /** Gets the last modified time of the file. */ 170 @SuppressWarnings("GoodTime") // should return a java.time.Instant getLastModifiedTime()171 public final synchronized long getLastModifiedTime() { 172 return lastModifiedTime; 173 } 174 175 /** Sets the creation time of the file. */ setCreationTime(long creationTime)176 final synchronized void setCreationTime(long creationTime) { 177 this.creationTime = creationTime; 178 } 179 180 /** Sets the last access time of the file. */ setLastAccessTime(long lastAccessTime)181 final synchronized void setLastAccessTime(long lastAccessTime) { 182 this.lastAccessTime = lastAccessTime; 183 } 184 185 /** Sets the last modified time of the file. */ setLastModifiedTime(long lastModifiedTime)186 final synchronized void setLastModifiedTime(long lastModifiedTime) { 187 this.lastModifiedTime = lastModifiedTime; 188 } 189 190 /** Sets the last access time of the file to the current time. */ updateAccessTime()191 final void updateAccessTime() { 192 setLastAccessTime(System.currentTimeMillis()); 193 } 194 195 /** Sets the last modified time of the file to the current time. */ updateModifiedTime()196 final void updateModifiedTime() { 197 setLastModifiedTime(System.currentTimeMillis()); 198 } 199 200 /** 201 * Returns the names of the attributes contained in the given attribute view in the file's 202 * attributes table. 203 */ getAttributeNames(String view)204 public final synchronized ImmutableSet<String> getAttributeNames(String view) { 205 if (attributes == null) { 206 return ImmutableSet.of(); 207 } 208 return ImmutableSet.copyOf(attributes.row(view).keySet()); 209 } 210 211 /** Returns the attribute keys contained in the attributes map for the file. */ 212 @VisibleForTesting getAttributeKeys()213 final synchronized ImmutableSet<String> getAttributeKeys() { 214 if (attributes == null) { 215 return ImmutableSet.of(); 216 } 217 218 ImmutableSet.Builder<String> builder = ImmutableSet.builder(); 219 for (Table.Cell<String, String, Object> cell : attributes.cellSet()) { 220 builder.add(cell.getRowKey() + ':' + cell.getColumnKey()); 221 } 222 return builder.build(); 223 } 224 225 /** Gets the value of the given attribute in the given view. */ 226 @NullableDecl getAttribute(String view, String attribute)227 public final synchronized Object getAttribute(String view, String attribute) { 228 if (attributes == null) { 229 return null; 230 } 231 return attributes.get(view, attribute); 232 } 233 234 /** Sets the given attribute in the given view to the given value. */ setAttribute(String view, String attribute, Object value)235 public final synchronized void setAttribute(String view, String attribute, Object value) { 236 if (attributes == null) { 237 attributes = HashBasedTable.create(); 238 } 239 attributes.put(view, attribute, value); 240 } 241 242 /** Deletes the given attribute from the given view. */ deleteAttribute(String view, String attribute)243 public final synchronized void deleteAttribute(String view, String attribute) { 244 if (attributes != null) { 245 attributes.remove(view, attribute); 246 } 247 } 248 249 /** Copies basic attributes (file times) from this file to the given file. */ copyBasicAttributes(File target)250 final synchronized void copyBasicAttributes(File target) { 251 target.setFileTimes(creationTime, lastModifiedTime, lastAccessTime); 252 } 253 setFileTimes( long creationTime, long lastModifiedTime, long lastAccessTime)254 private synchronized void setFileTimes( 255 long creationTime, long lastModifiedTime, long lastAccessTime) { 256 this.creationTime = creationTime; 257 this.lastModifiedTime = lastModifiedTime; 258 this.lastAccessTime = lastAccessTime; 259 } 260 261 /** Copies the attributes from this file to the given file. */ copyAttributes(File target)262 final synchronized void copyAttributes(File target) { 263 copyBasicAttributes(target); 264 target.putAll(attributes); 265 } 266 putAll(@ullableDecl Table<String, String, Object> attributes)267 private synchronized void putAll(@NullableDecl Table<String, String, Object> attributes) { 268 if (attributes != null && this.attributes != attributes) { 269 if (this.attributes == null) { 270 this.attributes = HashBasedTable.create(); 271 } 272 this.attributes.putAll(attributes); 273 } 274 } 275 276 @Override toString()277 public final String toString() { 278 return MoreObjects.toStringHelper(this).add("id", id()).toString(); 279 } 280 } 281