tikhomirov@64: /* tikhomirov@64: * Copyright (c) 2011 TMate Software Ltd tikhomirov@64: * tikhomirov@64: * This program is free software; you can redistribute it and/or modify tikhomirov@64: * it under the terms of the GNU General Public License as published by tikhomirov@64: * the Free Software Foundation; version 2 of the License. tikhomirov@64: * tikhomirov@64: * This program is distributed in the hope that it will be useful, tikhomirov@64: * but WITHOUT ANY WARRANTY; without even the implied warranty of tikhomirov@64: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the tikhomirov@64: * GNU General Public License for more details. tikhomirov@64: * tikhomirov@64: * For information on how to redistribute this software under tikhomirov@64: * the terms of a license other than GNU General Public License tikhomirov@102: * contact TMate Software at support@hg4j.com tikhomirov@64: */ tikhomirov@64: package org.tmatesoft.hg.core; tikhomirov@64: tikhomirov@74: import static org.tmatesoft.hg.repo.HgRepository.TIP; tikhomirov@64: tikhomirov@64: import java.util.ConcurrentModificationException; tikhomirov@64: import java.util.LinkedHashMap; tikhomirov@64: import java.util.LinkedList; tikhomirov@64: import java.util.List; tikhomirov@64: tikhomirov@64: import org.tmatesoft.hg.core.LogCommand.FileRevision; tikhomirov@74: import org.tmatesoft.hg.repo.HgManifest; tikhomirov@74: import org.tmatesoft.hg.repo.HgRepository; tikhomirov@64: import org.tmatesoft.hg.util.PathPool; tikhomirov@64: tikhomirov@64: tikhomirov@64: /** tikhomirov@64: * tikhomirov@64: * @author Artem Tikhomirov tikhomirov@64: * @author TMate Software Ltd. tikhomirov@64: */ tikhomirov@64: public class RepositoryTreeWalker { tikhomirov@64: tikhomirov@64: private final HgRepository repo; tikhomirov@64: private Path.Matcher matcher; tikhomirov@64: private int startRev = 0, endRev = TIP; tikhomirov@64: private Handler visitor; tikhomirov@64: private boolean needDirs = false; tikhomirov@64: tikhomirov@64: private final Mediator mediator = new Mediator(); tikhomirov@64: tikhomirov@64: public RepositoryTreeWalker(HgRepository hgRepo) { tikhomirov@64: this.repo = hgRepo; tikhomirov@64: } tikhomirov@64: tikhomirov@64: public RepositoryTreeWalker range(int rev1, int rev2) { tikhomirov@64: // if manifest range is different from that of changelog, need conversion utils (external?) tikhomirov@64: throw HgRepository.notImplemented(); tikhomirov@64: } tikhomirov@64: tikhomirov@67: public RepositoryTreeWalker revision(int rev) { tikhomirov@67: startRev = endRev = rev; tikhomirov@67: return this; tikhomirov@67: } tikhomirov@67: tikhomirov@64: public RepositoryTreeWalker dirs(boolean include) { tikhomirov@64: // XXX whether directories with directories only are include or not tikhomirov@64: // now lists only directories with files tikhomirov@64: needDirs = include; tikhomirov@64: return this; tikhomirov@64: } tikhomirov@64: tikhomirov@64: /** tikhomirov@64: * Limit manifest walk to a subset of files. tikhomirov@64: * @param pathMatcher - filter, pass null to clear. tikhomirov@64: * @return this instance for convenience tikhomirov@64: */ tikhomirov@64: public RepositoryTreeWalker match(Path.Matcher pathMatcher) { tikhomirov@64: matcher = pathMatcher; tikhomirov@64: return this; tikhomirov@64: } tikhomirov@64: tikhomirov@64: public void walk(Handler handler) { tikhomirov@64: if (handler == null) { tikhomirov@64: throw new IllegalArgumentException(); tikhomirov@64: } tikhomirov@64: if (visitor != null) { tikhomirov@64: throw new ConcurrentModificationException(); tikhomirov@64: } tikhomirov@64: try { tikhomirov@64: visitor = handler; tikhomirov@64: mediator.start(); tikhomirov@64: repo.getManifest().walk(startRev, endRev, mediator); tikhomirov@64: } finally { tikhomirov@92: mediator.done(); tikhomirov@64: visitor = null; tikhomirov@64: } tikhomirov@64: } tikhomirov@64: tikhomirov@64: /** tikhomirov@64: * Callback to walk file/directory tree of a revision tikhomirov@64: */ tikhomirov@64: public interface Handler { tikhomirov@64: void begin(Nodeid manifestRevision); tikhomirov@64: void dir(Path p); // optionally invoked (if walker was configured to spit out directories) prior to any files from this dir and subdirs tikhomirov@64: void file(FileRevision fileRevision); // XXX allow to check p is invalid (df.exists()) tikhomirov@64: void end(Nodeid manifestRevision); tikhomirov@64: } tikhomirov@64: tikhomirov@64: // I'd rather let RepositoryTreeWalker implement HgManifest.Inspector directly, but this pollutes API alot tikhomirov@64: private class Mediator implements HgManifest.Inspector { tikhomirov@64: private PathPool pathPool; tikhomirov@64: private List manifestContent; tikhomirov@64: private Nodeid manifestNodeid; tikhomirov@64: tikhomirov@64: public void start() { tikhomirov@64: pathPool = new PathPool(repo.getPathHelper()); tikhomirov@64: } tikhomirov@64: tikhomirov@64: public void done() { tikhomirov@64: manifestContent = null; tikhomirov@64: pathPool = null; tikhomirov@64: } tikhomirov@64: tikhomirov@64: public boolean begin(int revision, Nodeid nid) { tikhomirov@64: if (needDirs && manifestContent == null) { tikhomirov@64: manifestContent = new LinkedList(); tikhomirov@64: } tikhomirov@64: visitor.begin(manifestNodeid = nid); tikhomirov@64: return true; tikhomirov@64: } tikhomirov@64: public boolean end(int revision) { tikhomirov@64: if (needDirs) { tikhomirov@64: LinkedHashMap> breakDown = new LinkedHashMap>(); tikhomirov@64: for (FileRevision fr : manifestContent) { tikhomirov@64: Path filePath = fr.getPath(); tikhomirov@64: Path dirPath = pathPool.parent(filePath); tikhomirov@64: LinkedList revs = breakDown.get(dirPath); tikhomirov@64: if (revs == null) { tikhomirov@64: revs = new LinkedList(); tikhomirov@64: breakDown.put(dirPath, revs); tikhomirov@64: } tikhomirov@64: revs.addLast(fr); tikhomirov@64: } tikhomirov@64: for (Path dir : breakDown.keySet()) { tikhomirov@64: visitor.dir(dir); tikhomirov@64: for (FileRevision fr : breakDown.get(dir)) { tikhomirov@64: visitor.file(fr); tikhomirov@64: } tikhomirov@64: } tikhomirov@64: manifestContent.clear(); tikhomirov@64: } tikhomirov@64: visitor.end(manifestNodeid); tikhomirov@64: manifestNodeid = null; tikhomirov@64: return true; tikhomirov@64: } tikhomirov@64: public boolean next(Nodeid nid, String fname, String flags) { tikhomirov@64: Path p = pathPool.path(fname); tikhomirov@64: if (matcher != null && !matcher.accept(p)) { tikhomirov@64: return true; tikhomirov@64: } tikhomirov@64: FileRevision fr = new FileRevision(repo, nid, p); tikhomirov@64: if (needDirs) { tikhomirov@64: manifestContent.add(fr); tikhomirov@64: } else { tikhomirov@64: visitor.file(fr); tikhomirov@64: } tikhomirov@64: return true; tikhomirov@64: } tikhomirov@64: } tikhomirov@64: }