private Path normalizePathToProjectRoot(Path pathRelativeToProjectRoot)
     throws NoSuchFileException {
   if (!exists(pathRelativeToProjectRoot)) {
     throw new NoSuchFileException(pathRelativeToProjectRoot.toString());
   }
   return MorePaths.normalize(pathRelativeToProjectRoot);
 }
 @Override
 public long getLastModifiedTime(Path path) throws IOException {
   Path normalizedPath = MorePaths.normalize(path);
   if (!exists(normalizedPath)) {
     throw new NoSuchFileException(path.toString());
   }
   return Preconditions.checkNotNull(fileLastModifiedTimes.get(normalizedPath)).toMillis();
 }
 @Override
 public Path setLastModifiedTime(Path path, FileTime time) throws IOException {
   Path normalizedPath = MorePaths.normalize(path);
   if (!exists(normalizedPath)) {
     throw new NoSuchFileException(path.toString());
   }
   fileLastModifiedTimes.put(normalizedPath, time);
   return normalizedPath;
 }
 @Override
 public void deleteRecursivelyIfExists(Path path) throws IOException {
   Path normalizedPath = MorePaths.normalize(path);
   for (Iterator<Path> iterator = fileContents.keySet().iterator(); iterator.hasNext(); ) {
     Path subPath = iterator.next();
     if (subPath.startsWith(normalizedPath)) {
       fileAttributes.remove(MorePaths.normalize(subPath));
       fileLastModifiedTimes.remove(MorePaths.normalize(subPath));
       iterator.remove();
     }
   }
   for (Iterator<Path> iterator = symLinks.keySet().iterator(); iterator.hasNext(); ) {
     Path subPath = iterator.next();
     if (subPath.startsWith(normalizedPath)) {
       iterator.remove();
     }
   }
   fileLastModifiedTimes.remove(path);
   directories.remove(path);
 }
 @Override
 public void move(Path source, Path target, CopyOption... options) throws IOException {
   fileContents.put(MorePaths.normalize(target), fileContents.remove(MorePaths.normalize(source)));
   fileAttributes.put(
       MorePaths.normalize(target), fileAttributes.remove(MorePaths.normalize(source)));
   fileLastModifiedTimes.put(
       MorePaths.normalize(target), fileLastModifiedTimes.remove(MorePaths.normalize(source)));
 }
  @Override
  public void writeBytesToPath(byte[] bytes, Path path, FileAttribute<?>... attrs)
      throws IOException {
    Path normalizedPath = MorePaths.normalize(path);
    fileContents.put(normalizedPath, Preconditions.checkNotNull(bytes));
    fileAttributes.put(normalizedPath, ImmutableSet.copyOf(attrs));

    Path directory = normalizedPath.getParent();
    while (directory != null) {
      directories.add(directory);
      directory = directory.getParent();
    }
    fileLastModifiedTimes.put(normalizedPath, FileTime.fromMillis(clock.currentTimeMillis()));
  }
 @Override
 public boolean isDirectory(Path path, LinkOption... linkOptions) {
   return directories.contains(MorePaths.normalize(path));
 }
 @Override
 public boolean isFile(Path path) {
   return fileContents.containsKey(MorePaths.normalize(path));
 }
 private void rmFile(Path path) {
   fileContents.remove(MorePaths.normalize(path));
   fileAttributes.remove(MorePaths.normalize(path));
   fileLastModifiedTimes.remove(MorePaths.normalize(path));
 }
 private byte[] getFileBytes(Path path) {
   return Preconditions.checkNotNull(fileContents.get(MorePaths.normalize(path)));
 }