Move FileStateValue.Type to Metadata; add Metadata.getType()

This is in preparation for merging FileArtifactValue and FileStateValue.

Progress on #3360.

PiperOrigin-RevId: 179832948
diff --git a/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java b/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
index 0b272fb..c241c56 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/AbstractAction.java
@@ -428,10 +428,13 @@
     for (Artifact input : getMandatoryInputs()) {
       // Assume that if the file did not exist, we would not have gotten here.
       try {
-        if (input.isSourceArtifact() && !metadataProvider.getMetadata(input).isFile()) {
-          eventHandler.handle(Event.warn(getOwner().getLocation(), "input '"
-              + input.prettyPrint() + "' to " + getOwner().getLabel()
-              + " is a directory; dependency checking of directories is unsound"));
+        if (input.isSourceArtifact()
+            && metadataProvider.getMetadata(input).getType().isDirectory()) {
+          // TODO(ulfjack): What about dependency checking of special files?
+          eventHandler.handle(Event.warn(getOwner().getLocation(),
+              String.format(
+                  "input '%s' to %s is a directory; dependency checking of directories is unsound",
+                  input.prettyPrint(), getOwner().getLabel())));
         }
       } catch (IOException e) {
         throw new UserExecException(e);
diff --git a/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java b/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
index 8e4dc88..b6957cf 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/ActionCacheChecker.java
@@ -51,15 +51,21 @@
  * otherwise lightweight, and should be constructed anew and discarded for each build request.
  */
 public class ActionCacheChecker {
+  private static final byte[] EMPTY_DIGEST = new byte[0];
   private static final Metadata CONSTANT_METADATA = new Metadata() {
     @Override
+    public FileStateType getType() {
+      return FileStateType.REGULAR_FILE;
+    }
+
+    @Override
     public boolean isFile() {
-      return false;
+      return true;
     }
 
     @Override
     public byte[] getDigest() {
-      throw new UnsupportedOperationException();
+      return EMPTY_DIGEST;
     }
 
     @Override
diff --git a/src/main/java/com/google/devtools/build/lib/actions/FileStateType.java b/src/main/java/com/google/devtools/build/lib/actions/FileStateType.java
new file mode 100644
index 0000000..8e339e4
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/actions/FileStateType.java
@@ -0,0 +1,79 @@
+// Copyright 2017 The Bazel Authors. All rights reserved.
+//
+// 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.devtools.build.lib.actions;
+
+/** An enum indicating the type of a path on the file system. */
+public enum FileStateType {
+  REGULAR_FILE("file"),
+  /**
+   * A special file such as a socket, fifo, or device. See
+   * {@link com.google.devtools.build.lib.vfs.FileStatus#isSpecialFile}.
+   */
+  SPECIAL_FILE("special file"),
+  DIRECTORY("directory"),
+  SYMLINK("symlink"),
+  NONEXISTENT("non-existent path");
+
+  private final String name;
+
+  private FileStateType(String name) {
+    this.name = name;
+  }
+
+  public String getHumanReadableName() {
+    return name;
+  }
+
+  /** Returns true if this type does not correspond to a non-existent path. */
+  public boolean exists() {
+    return this != NONEXISTENT;
+  }
+
+  /** Returns true if this value corresponds to a symlink. */
+  public boolean isSymlink() {
+    return this == SYMLINK;
+  }
+
+  /**
+   * Returns true if this value corresponds to a regular file. If so, its parent directory is
+   * guaranteed to exist.
+   */
+  public boolean isFile() {
+    return this == REGULAR_FILE;
+  }
+
+  /**
+   * Returns true if this value corresponds to a special file. If so, its parent directory is
+   * guaranteed to exist.
+   */
+  public boolean isSpecialFile() {
+    return this == SPECIAL_FILE;
+  }
+
+  /**
+   * Returns true if this value corresponds to a regular or special file. If so, its parent
+   * directory is guaranteed to exist.
+   */
+  public boolean isRegularOrSpecialFile() {
+    return this == REGULAR_FILE || this == FileStateType.SPECIAL_FILE;
+  }
+
+  /**
+   * Returns true if the file is a directory. If so, its parent directory is guaranteed to exist.
+   */
+  public boolean isDirectory() {
+    return this == DIRECTORY;
+  }
+}
\ No newline at end of file
diff --git a/src/main/java/com/google/devtools/build/lib/actions/MetadataProvider.java b/src/main/java/com/google/devtools/build/lib/actions/MetadataProvider.java
index 40d54b6..1538286 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/MetadataProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/MetadataProvider.java
@@ -29,6 +29,10 @@
    * then t >= p. Aside from these properties, t can be any value and may vary arbitrarily across
    * calls.
    *
+   * <p>Returned {@link Metadata} instance correspond to the final target of a symlink, and
+   * therefore must not have a type of
+   * {@link com.google.devtools.build.lib.actions.FileStateType#SYMLINK} themselves.
+   *
    * The return value is owned by the cache and must not be modified.
    *
    * @param input the input to retrieve the digest for
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java b/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
index 642bd29..c14cb03 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/Metadata.java
@@ -14,12 +14,12 @@
 
 package com.google.devtools.build.lib.actions.cache;
 
+import com.google.devtools.build.lib.actions.FileStateType;
+
 /**
- * An interface to represent the state of a file (or directory). This is used to determine whether a
- * file has changed or not.
- *
- * <p>NB! Several other parts of Blaze are relying on the fact that metadata uses mtime and not
- * ctime. If metadata is ever changed to use ctime, all uses of Metadata must be carefully examined.
+ * An interface to represent the state of a file system object for the execution phase. This is not
+ * used by Skyframe for invalidation, it is primarily used by the action cache and the various
+ * {@link com.google.devtools.build.lib.exec.SpawnRunner} implementations.
  */
 public interface Metadata {
   /**
@@ -30,6 +30,14 @@
   }
 
   /**
+   * The type of the underlying file system object. If it is a regular file, then it is
+   * guaranteed to have a digest. Otherwise it does not have a digest.
+   */
+  default FileStateType getType() {
+    return isFile() ? FileStateType.REGULAR_FILE : FileStateType.DIRECTORY;
+  }
+
+  /**
    * Whether the underlying file system object is a file or a symlink to a file, rather than a
    * directory. All files are guaranteed to have a digest, and {@link #getDigest} must only be
    * called on files.
@@ -40,7 +48,7 @@
    * Returns the file's digest; must only be called on objects for which {@link #isFile} returns
    * true.
    *
-   * <p>The return value is owned by the cache and must not be modified.
+   * <p>The return value is owned by this object and must not be modified.
    */
   byte[] getDigest();