package com.beijunyi.parallelgit.filesystem.io; import java.io.IOException; import java.util.*; import javax.annotation.Nonnull; import com.beijunyi.parallelgit.filesystem.GitFileSystem; import com.beijunyi.parallelgit.utils.io.GitFileEntry; import org.eclipse.jgit.lib.FileMode; import static com.beijunyi.parallelgit.filesystem.utils.GfsPathUtils.*; public class GfsChangesCollector { private static final GfsChange DELETE_NODE = new DeleteNode(); private static final GfsChange PREPARE_DIRECTORY = new MakeDirectory(); private final Map<String, GfsChange> changes = new HashMap<>(); private final Map<String, Set<String>> changedDirs = new HashMap<>(); private final Queue<DirectoryNode> dirs = new LinkedList<>(); private final Queue<String> paths = new LinkedList<>(); public boolean isEmpty() { return changes.isEmpty(); } public void addChange(String path, GfsChange change) { if(changes.containsKey(path)) throw new IllegalStateException(); changes.put(path, change); addChangedDirectory(path); } public void addChange(String path, GitFileEntry entry) { GfsChange change; if(entry.isMissing()) change = DELETE_NODE; else if(entry.isVirtualSubtree()) change = PREPARE_DIRECTORY; else change = new UpdateNode(entry); addChange(path, change); } public void addChange(String path, byte[] bytes, FileMode mode) { addChange(path, new UpdateFile(bytes, mode)); } public void applyTo(GitFileSystem gfs) throws IOException { dirs.add(gfs.getFileStore().getRoot()); paths.add("/"); while(!dirs.isEmpty()) { DirectoryNode dir = dirs.poll(); String path = paths.poll(); applyChangesToDir(dir, path); } } private void addChangedDirectory(String path) { if(isRoot(path)) return; int parentLength = path.lastIndexOf('/'); if(parentLength >= 0) { String parent = path.substring(0, parentLength == 0 ? 1 : parentLength); String filename = path.substring(parentLength + 1); Set<String> siblings = childrenOf(parent); if(siblings.add(filename)) addChangedDirectory(parent); } } @Nonnull private Set<String> childrenOf(String parent) { Set<String> ret = changedDirs.get(parent); if(ret == null) { ret = new HashSet<>(); changedDirs.put(parent, ret); } return ret; } private void applyChangesToDir(DirectoryNode dir, String path) throws IOException { String prefix = addTrailingSlash(path); for(String childName : childrenOf(path)) { String childPath = prefix + childName; GfsChange change = changes.get(childPath); if(change != null) change.applyTo(dir, childName); if(changedDirs.containsKey(childPath)) addSubDirectoryToQueue(childPath, childName, dir); } } private void addSubDirectoryToQueue(String childPath, String childName, DirectoryNode dir) throws IOException { DirectoryNode child = (DirectoryNode) dir.getChild(childName); if(child == null) { child = DirectoryNode.newDirectory(dir); dir.addChild(childName, child, false); } dirs.add(child); paths.add(childPath); } }