code: package com.beijunyi.parallelgit.filesystem.io;
  
  import java.io.IOException;
  import java.util.*;
  import javax.annotation.Nonnull;
  import javax.annotation.Nullable;
  
  import com.beijunyi.parallelgit.filesystem.GfsFileStore;
  import com.beijunyi.parallelgit.filesystem.GitFileSystem;
  import org.eclipse.jgit.dircache.DirCacheEntry;
  import org.eclipse.jgit.lib.FileMode;
  import org.eclipse.jgit.lib.ObjectId;
  import org.eclipse.jgit.lib.ObjectReader;
  import org.eclipse.jgit.treewalk.AbstractTreeIterator;
  import org.eclipse.jgit.treewalk.WorkingTreeIterator;
  import org.eclipse.jgit.treewalk.WorkingTreeOptions;
  
  import static java.lang.System.arraycopy;
  import static java.util.Collections.*;
  import static org.eclipse.jgit.lib.Constants.*;
  
  public class GfsTreeIterator extends WorkingTreeIterator {
  
    private final List<GfsTreeEntry> files;
    private int index = -1;
    private ObjectId id;
  
    private GfsTreeIterator(List<GfsTreeEntry> files, GfsTreeIterator parent) {
      super(parent);
      this.files = files;
      next(1);
    }
  
    private GfsTreeIterator(List<GfsTreeEntry> files) {
      super((WorkingTreeOptions) null);
      this.files = files;
      next(1);
    }
  
    private GfsTreeIterator(DirectoryNode node) throws IOException {
      this(GfsTreeEntry.listChildren(node));
    }
  
    private GfsTreeIterator(GfsFileStore store) throws IOException {
      this(store.getRoot());
    }
  
    private GfsTreeIterator(GitFileSystem gfs) throws IOException {
      this(gfs.getFileStore());
    }
  
    @Nonnull
    public static GfsTreeIterator iterateRoot(GitFileSystem gfs) throws IOException {
      return new GfsTreeIterator(gfs);
    }
  
    @Override
    public boolean isModified(@Nullable DirCacheEntry entry, boolean forceContentCheck, ObjectReader reader) throws IOException {
      GfsTreeEntry current = currentEntry();
      return entry == null || !current.getId().equals(entry.getObjectId()) || !current.getMode().equals(entry.getFileMode());
    }
  
    @Override
    public boolean hasId() {
      return index >= 0 && index < files.size();
    }
  
    @Override
    public byte[] idBuffer() {
      byte[] ret = new byte[OBJECT_ID_LENGTH];
      id.copyRawTo(ret, 0);
      return ret;
    }
  
    @Override
    public int idOffset() {
      return 0;
    }
  
    @Nonnull
    @Override
    public AbstractTreeIterator createSubtreeIterator(ObjectReader reader) throws IOException {
      GfsTreeEntry entry = currentEntry();
      return new GfsTreeIterator(entry.listChildren(), this);
    }
  
    @Override
    public boolean first() {
      return index == 0;
    }
  
    @Override
    public boolean eof() {
      return index == files.size();
    }
  
    @Override
    public void next(int delta) {
      index = Math.min(files.size(), index + delta);
      if(!eof()) readEntry();
    }
  
    @Override
    public void back(int delta) {
      index = Math.max(0, index - delta);
      readEntry();
    }
  
    @Nonnull
    private GfsTreeEntry currentEntry() {
      return files.get(index);
    }
  
    private void readEntry() {
      GfsTreeEntry entry = currentEntry();
  
      mode = entry.getMode().getBits();
      id = entry.getId();
  
      byte[] name = encode(entry.getName());
      ensurePathCapacity(pathOffset + name.length, pathOffset);
      arraycopy(name, 0, path, pathOffset, name.length);
      pathLen = pathOffset + name.length;
    }
  
    private static class GfsTreeEntry {
      private final String name;
      private final Node node;
  
      private GfsTreeEntry(String name, Node node) {
        this.name = name;
        this.node = node;
      }
  
      @Nonnull
      public static GfsTreeEntry forNode(String name, Node node) {
        return new GfsTreeEntry(name, node);
      }
  
      @Nonnull
      public String getName() {
        return name;
      }
  
      @Nonnull
      public ObjectId getId() {
        try {
          return node.getObjectId(false);
        } catch(IOException e) {
          throw new IllegalStateException(e);
        }
      }
  
      @Nonnull
      public FileMode getMode() {
        return node.getMode();
      }
  
      @Nonnull
      public List<GfsTreeEntry> listChildren() throws IOException {
        if(!node.isDirectory())
          throw new IllegalStateException();
        return listChildren((DirectoryNode) node);
      }
  
      @Nonnull
      public static List<GfsTreeEntry> listChildren(DirectoryNode dir) throws IOException {
        List<GfsTreeEntry> ret = new ArrayList<>();
        for(Map.Entry<String, Node> child : dir.getData().entrySet()) {
          Node node = child.getValue();
          if(!node.isTrivial()) ret.add(forNode(child.getKey(), node));
        }
        sort(ret, TreeEntryComparator.ASCENDING);
        return unmodifiableList(ret);
      }
    }
  
    private static class TreeEntryComparator implements Comparator<GfsTreeEntry> {
  
      private static TreeEntryComparator ASCENDING = new TreeEntryComparator();
  
      @Override
      public int compare(GfsTreeEntry o1, GfsTreeEntry o2) {
        return o1.getName().compareTo(o2.getName());
      }
  
    }
  
  }
  