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