// Copyright 2014 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.skyframe;

import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;

import java.util.Objects;

/**
 * A value that represents the list of files in a given directory under a given package path root.
 * Anything in Skyframe that cares about the contents of a directory should have a dependency
 * on the corresponding {@link DirectoryListingValue}.
 *
 * <p>This value only depends on the FileValue corresponding to the directory. In particular, note
 * that it does not depend on any of its child entries.
 *
 * <p>Note that symlinks in dirents are <b>not</b> expanded. Dependents of the value are responsible
 * for expanding the symlink entries by referring to FileValues that correspond to the symlinks.
 * This is a little onerous, but correct: we do not need to reread the directory when a symlink
 * inside it changes, therefore this value should not be invalidated in that case.
 */
@Immutable
@ThreadSafe
public abstract class DirectoryListingValue implements SkyValue {

  /**
   * Returns the directory entries for this directory, in a stable order.
   *
   * <p>Symlinks are not expanded.
   */
  public Dirents getDirents() {
    return getDirectoryListingStateValue().getDirents();
  }

  public abstract DirectoryListingStateValue getDirectoryListingStateValue();

  /**
   * Returns a {@link SkyKey} for getting the directory entries of the given directory. The
   * given path is assumed to be an existing directory (e.g. via {@link FileValue#isDirectory} or
   * from a directory listing on its parent directory).
   */
  @ThreadSafe
  static SkyKey key(RootedPath directoryUnderRoot) {
    return SkyKey.create(SkyFunctions.DIRECTORY_LISTING, directoryUnderRoot);
  }

  static DirectoryListingValue value(RootedPath dirRootedPath, FileValue dirFileValue,
      DirectoryListingStateValue realDirectoryListingStateValue) {
    return dirFileValue.realRootedPath().equals(dirRootedPath)
        ? new RegularDirectoryListingValue(realDirectoryListingStateValue)
        : new DifferentRealPathDirectoryListingValue(dirFileValue.realRootedPath(),
            realDirectoryListingStateValue);
  }

  /** Normal {@link DirectoryListingValue}. */
  @ThreadSafe
  public static final class RegularDirectoryListingValue extends DirectoryListingValue {

    private final DirectoryListingStateValue directoryListingStateValue;

    public RegularDirectoryListingValue(DirectoryListingStateValue directoryListingStateValue) {
      this.directoryListingStateValue = directoryListingStateValue;
    }

    @Override
    public DirectoryListingStateValue getDirectoryListingStateValue() {
      return directoryListingStateValue;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      }
      if (!(obj instanceof RegularDirectoryListingValue)) {
        return false;
      }
      RegularDirectoryListingValue other = (RegularDirectoryListingValue) obj;
      return directoryListingStateValue.equals(other.directoryListingStateValue);
    }

    @Override
    public int hashCode() {
      return directoryListingStateValue.hashCode();
    }
  }

  /** A {@link DirectoryListingValue} with a different root. */
  @ThreadSafe
  public static final class DifferentRealPathDirectoryListingValue extends DirectoryListingValue {

    private final RootedPath realDirRootedPath;
    private final DirectoryListingStateValue directoryListingStateValue;

    public DifferentRealPathDirectoryListingValue(RootedPath realDirRootedPath,
        DirectoryListingStateValue directoryListingStateValue) {
      this.realDirRootedPath = realDirRootedPath;
      this.directoryListingStateValue = directoryListingStateValue;
    }

    public RootedPath getRealDirRootedPath() {
      return realDirRootedPath;
    }

    @Override
    public DirectoryListingStateValue getDirectoryListingStateValue() {
      return directoryListingStateValue;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj) {
        return true;
      }
      if (!(obj instanceof DifferentRealPathDirectoryListingValue)) {
        return false;
      }
      DifferentRealPathDirectoryListingValue other = (DifferentRealPathDirectoryListingValue) obj;
      return realDirRootedPath.equals(other.realDirRootedPath)
          && directoryListingStateValue.equals(other.directoryListingStateValue);
    }

    @Override
    public int hashCode() {
      return Objects.hash(realDirRootedPath, directoryListingStateValue);
    }
  }
}

@@ -1024,21 +1024,48 @@
 the 
-list of fil
+dirents (name and type of child entri
 es
+)
  in 
@@ -1089,16 +1089,19 @@
  under a
+%0A *
  given p
@@ -1116,20 +1116,72 @@
 ath root
-.%0A *
+, fully accounting for symlinks in the directory's path.
  Anythin
@@ -1184,16 +1184,19 @@
 thing in
+%0A *
  Skyfram
@@ -1270,18 +1270,18 @@
 ency
-%0A *
  on the
+%0A *
  cor
@@ -1335,244 +1335,109 @@
  %3Cp%3E
-This value only depends on the FileValue corresponding to the directory. In particular, note%0A * that it does not depend on any of its child entries.%0A *%0A * %3Cp%3ENote that symlinks in dirents are %3Cb%3Enot%3C/b%3E expanded. Dependents of the value
+Note that dirents that are themselves symlinks are %3Cb%3Enot%3C/b%3E resolved. Consumers of such a%0A * dirent
  are
@@ -1452,22 +1452,19 @@
 ible
-%0A *
  for 
-expand
+resolv
 ing 
@@ -1483,70 +1483,46 @@
 entr
-ies by referring to FileValues that correspond to the symlinks
+y via an appropriate %7B@link FileValue%7D
 .%0A *
@@ -1639,24 +1639,118 @@
 nges
-, therefore this
+ (or, more generally, when the *contents* of a dirent changes), therefore the%0A * %7B@link DirectoryListingValue%7D
  val

Improve class Javadoc of DirectoryListingValue.