blob: 99a4f597d77abf4df07dfedc2422042a5384748d [file] [log] [blame]
/*
* Copyright 2013 Google Inc.
*
* 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 com.google.common.jimfs;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Table;
import java.io.IOException;
import java.util.concurrent.locks.ReadWriteLock;
import javax.annotation.Nullable;
/**
* A file object, containing both the file's metadata and content.
*
* @author Colin Decker
*/
public abstract class File {
private final int id;
private int links;
private long creationTime;
private long lastAccessTime;
private long lastModifiedTime;
@Nullable // null when only the basic view is used (default)
private Table<String, String, Object> attributes;
File(int id) {
this.id = id;
long now = System.currentTimeMillis(); // TODO(cgdecker): Use a Clock
this.creationTime = now;
this.lastAccessTime = now;
this.lastModifiedTime = now;
}
/**
* Returns the ID of this file.
*/
public int id() {
return id;
}
/**
* Returns the size, in bytes, of this file's content. Directories and symbolic links have a size
* of 0.
*/
public long size() {
return 0;
}
/**
* Returns whether or not this file is a directory.
*/
public final boolean isDirectory() {
return this instanceof Directory;
}
/**
* Returns whether or not this file is a regular file.
*/
public final boolean isRegularFile() {
return this instanceof RegularFile;
}
/**
* Returns whether or not this file is a symbolic link.
*/
public final boolean isSymbolicLink() {
return this instanceof SymbolicLink;
}
/**
* Creates a new file of the same type as this file with the given ID. Does not copy the content
* of this file unless the cost of copying the content is minimal. This is because this method is
* called with a hold on the file system's lock.
*/
abstract File copyWithoutContent(int id);
/**
* Copies the content of this file to the given file. The given file must be the same type of
* file as this file and should have no content.
*
* <p>This method is used for copying the content of a file after copying the file itself. Does
* nothing by default.
*/
void copyContentTo(File file) throws IOException {}
/**
* Returns the read-write lock for this file's content, or {@code null} if there is no content
* lock.
*/
@Nullable
ReadWriteLock contentLock() {
return null;
}
/**
* Called when a stream or channel to this file is opened.
*/
void opened() {}
/**
* Called when a stream or channel to this file is closed. If there are no more streams or
* channels open to the file and it has been deleted, its contents may be deleted.
*/
void closed() {}
/**
* Called when (a single link to) this file is deleted. There may be links remaining. Does
* nothing by default.
*/
void deleted() {}
/**
* Returns whether or not this file is a root directory of the file system.
*/
final boolean isRootDirectory() {
// only root directories have their parent link pointing to themselves
return isDirectory() && equals(((Directory) this).parent());
}
/**
* Returns the current count of links to this file.
*/
public synchronized final int links() {
return links;
}
/**
* Called when this file has been linked in a directory. The given entry is the new directory
* entry that links to this file.
*/
void linked(DirectoryEntry entry) {
checkNotNull(entry);
}
/**
* Called when this file has been unlinked from a directory, either for a move or delete.
*/
void unlinked() {}
/**
* Increments the link count for this file.
*/
synchronized final void incrementLinkCount() {
links++;
}
/**
* Decrements the link count for this file.
*/
synchronized final void decrementLinkCount() {
links--;
}
/**
* Gets the creation time of the file.
*/
public synchronized final long getCreationTime() {
return creationTime;
}
/**
* Gets the last access time of the file.
*/
public synchronized final long getLastAccessTime() {
return lastAccessTime;
}
/**
* Gets the last modified time of the file.
*/
public synchronized final long getLastModifiedTime() {
return lastModifiedTime;
}
/**
* Sets the creation time of the file.
*/
synchronized final void setCreationTime(long creationTime) {
this.creationTime = creationTime;
}
/**
* Sets the last access time of the file.
*/
synchronized final void setLastAccessTime(long lastAccessTime) {
this.lastAccessTime = lastAccessTime;
}
/**
* Sets the last modified time of the file.
*/
synchronized final void setLastModifiedTime(long lastModifiedTime) {
this.lastModifiedTime = lastModifiedTime;
}
/**
* Sets the last access time of the file to the current time.
*/
final void updateAccessTime() {
setLastAccessTime(System.currentTimeMillis());
}
/**
* Sets the last modified time of the file to the current time.
*/
final void updateModifiedTime() {
setLastModifiedTime(System.currentTimeMillis());
}
/**
* Returns the names of the attributes contained in the given attribute view in the file's
* attributes table.
*/
public synchronized final ImmutableSet<String> getAttributeNames(String view) {
if (attributes == null) {
return ImmutableSet.of();
}
return ImmutableSet.copyOf(attributes.row(view).keySet());
}
/**
* Returns the attribute keys contained in the attributes map for the file.
*/
@VisibleForTesting
synchronized final ImmutableSet<String> getAttributeKeys() {
if (attributes == null) {
return ImmutableSet.of();
}
ImmutableSet.Builder<String> builder = ImmutableSet.builder();
for (Table.Cell<String, String, Object> cell : attributes.cellSet()) {
builder.add(cell.getRowKey() + ':' + cell.getColumnKey());
}
return builder.build();
}
/**
* Gets the value of the given attribute in the given view.
*/
@Nullable
public synchronized final Object getAttribute(String view, String attribute) {
if (attributes == null) {
return null;
}
return attributes.get(view, attribute);
}
/**
* Sets the given attribute in the given view to the given value.
*/
public synchronized final void setAttribute(String view, String attribute, Object value) {
if (attributes == null) {
attributes = HashBasedTable.create();
}
attributes.put(view, attribute, value);
}
/**
* Deletes the given attribute from the given view.
*/
public synchronized final void deleteAttribute(String view, String attribute) {
if (attributes != null) {
attributes.remove(view, attribute);
}
}
/**
* Copies basic attributes (file times) from this file to the given file.
*/
synchronized final void copyBasicAttributes(File target) {
target.setFileTimes(creationTime, lastModifiedTime, lastAccessTime);
}
private synchronized void setFileTimes(
long creationTime, long lastModifiedTime, long lastAccessTime) {
this.creationTime = creationTime;
this.lastModifiedTime = lastModifiedTime;
this.lastAccessTime = lastAccessTime;
}
/**
* Copies the attributes from this file to the given file.
*/
synchronized final void copyAttributes(File target) {
copyBasicAttributes(target);
target.putAll(attributes);
}
private synchronized void putAll(@Nullable Table<String, String, Object> attributes) {
if (attributes != null && this.attributes != attributes) {
if (this.attributes == null) {
this.attributes = HashBasedTable.create();
}
this.attributes.putAll(attributes);
}
}
@Override
public final String toString() {
return MoreObjects.toStringHelper(this)
.add("id", id())
.toString();
}
}