code: package com.beijunyi.parallelgit.utils;
  
  import java.io.IOException;
  import java.io.InputStream;
  import java.nio.file.NoSuchFileException;
  import javax.annotation.Nonnull;
  import javax.annotation.Nullable;
  
  import com.beijunyi.parallelgit.utils.io.BlobSnapshot;
  import org.eclipse.jgit.lib.*;
  import org.eclipse.jgit.treewalk.TreeWalk;
  
  import static org.eclipse.jgit.lib.FileMode.*;
  
  public final class TreeUtils {
  
    @Nonnull
    public static String normalizeNodePath(String path) {
      if(path.startsWith("/"))
        return path.substring(1);
      if(path.endsWith("/"))
        return path.substring(0, path.length() - 1);
      return path;
    }
  
    @Nonnull
    public static TreeWalk newTreeWalk(AnyObjectId tree, ObjectReader reader) throws IOException {
      TreeWalk tw = new TreeWalk(reader);
      tw.reset(tree);
      return tw;
    }
  
    @Nonnull
    public static TreeWalk newTreeWalk(AnyObjectId tree, Repository repo) throws IOException {
      return newTreeWalk(tree, repo.newObjectReader());
    }
  
    @Nullable
    public static TreeWalk forPath(String path, AnyObjectId tree, ObjectReader reader) throws IOException {
      return TreeWalk.forPath(reader, normalizeNodePath(path), tree);
    }
  
    @Nullable
    public static TreeWalk forPath(String path, AnyObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return forPath(path, tree, reader);
      }
    }
  
    public static boolean exists(String path, AnyObjectId tree, ObjectReader reader) throws IOException {
      return forPath(path, tree, reader) != null;
    }
  
    public static boolean exists(String path, AnyObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return exists(path, tree, reader);
      }
    }
  
    @Nullable
    public static ObjectId getObjectId(TreeWalk tw) {
      return tw.getObjectId(0);
    }
  
    @Nullable
    public static ObjectId getObjectId(String path, AnyObjectId tree, ObjectReader reader) throws IOException {
      try(TreeWalk tw = forPath(path, tree, reader)) {
        return tw != null ? getObjectId(tw) : null;
      }
    }
  
    @Nullable
    public static ObjectId getObjectId(String path, AnyObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return getObjectId(path, tree, reader);
      }
    }
  
    @Nullable
    public static FileMode getFileMode(TreeWalk tw) {
      return tw.getFileMode(0);
    }
  
    @Nullable
    public static FileMode getFileMode(String path, AnyObjectId tree, ObjectReader reader) throws IOException {
      try(TreeWalk tw = forPath(path, tree, reader)) {
        return tw != null ? getFileMode(tw) : null;
      }
    }
  
    @Nullable
    public static FileMode getFileMode(String path, AnyObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return getFileMode(path, tree, reader);
      }
    }
  
    @Nonnull
    public static InputStream openFile(String path, AnyObjectId tree, ObjectReader reader) throws IOException {
      AnyObjectId blobId = getObjectId(path, tree, reader);
      if(blobId == null)
        throw new NoSuchFileException(path);
      return BlobUtils.openBlob(blobId, reader);
    }
  
    @Nonnull
    public static InputStream openFile(String path, AnyObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return openFile(path, tree, reader);
      }
    }
  
    @Nonnull
    public static BlobSnapshot readFile(String path, ObjectId tree, ObjectReader reader) throws IOException {
      ObjectId blobId = getObjectId(path, tree, reader);
      if(blobId == null)
        throw new NoSuchFileException(path);
      return BlobUtils.readBlob(blobId, reader);
    }
  
    @Nonnull
    public static BlobSnapshot readFile(String path, ObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return readFile(path, tree, reader);
      }
    }
  
    public static boolean isDirectory(TreeWalk tw) {
      return TREE.equals(getFileMode(tw));
    }
  
    public static boolean isDirectory(String path, AnyObjectId tree, ObjectReader reader) throws IOException {
      try(TreeWalk tw = forPath(path, tree, reader)) {
        return tw != null && isDirectory(tw);
      }
    }
  
    public static boolean isDirectory(String path, AnyObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return isDirectory(path, tree, reader);
      }
    }
  
    public static boolean isFile(TreeWalk tw) {
      return REGULAR_FILE.equals(getFileMode(tw)) || EXECUTABLE_FILE.equals(getFileMode(tw));
    }
  
    public static boolean isFile(String path, AnyObjectId tree, ObjectReader reader) throws IOException {
      try(TreeWalk tw = forPath(path, tree, reader)) {
        return tw != null && isFile(tw);
      }
    }
  
    public static boolean isFile(String path, AnyObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return isFile(path, tree, reader);
      }
    }
  
    public static boolean isSymbolicLink(TreeWalk tw) {
      return SYMLINK.equals(getFileMode(tw));
    }
  
    public static boolean isSymbolicLink(String path, AnyObjectId tree, ObjectReader reader) throws IOException {
      try(TreeWalk tw = forPath(path, tree, reader)) {
        return tw != null && isSymbolicLink(tw);
      }
    }
  
    public static boolean isSymbolicLink(String path, AnyObjectId tree, Repository repo) throws IOException {
      try(ObjectReader reader = repo.newObjectReader()) {
        return isSymbolicLink(path, tree, reader);
      }
    }
  
    @Nonnull
    public static ObjectId insertTree(TreeFormatter tf, Repository repo) throws IOException {
      try(ObjectInserter inserter = repo.newObjectInserter()) {
        ObjectId treeId = inserter.insert(tf);
        inserter.flush();
        return treeId;
      }
    }
  
  
  }
  