Mercurial > jhg
changeset 131:aa1629f36482
Renamed .core classes to start with Hg prefix
line wrap: on
line diff
--- a/TODO Wed Feb 16 20:33:31 2011 +0100 +++ b/TODO Wed Feb 16 20:47:56 2011 +0100 @@ -47,7 +47,7 @@ - HgRepository constants (TIP, BAD, WC) to HgRevisions enum * defects - ConfigFile to strip comments from values (#) + - ConfigFile to strip comments from values (#) Proposed: - LogCommand.revision(int... rev)+ to walk selected revisions only (list->sort(array) on execute, binary search)
--- a/cmdline/org/tmatesoft/hg/console/Log.java Wed Feb 16 20:33:31 2011 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Log.java Wed Feb 16 20:47:56 2011 +0100 @@ -21,8 +21,8 @@ import java.util.List; import org.tmatesoft.hg.core.HgChangeset; -import org.tmatesoft.hg.core.LogCommand; -import org.tmatesoft.hg.core.LogCommand.FileRevision; +import org.tmatesoft.hg.core.HgLogCommand; +import org.tmatesoft.hg.core.HgLogCommand.FileRevision; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.core.Path; import org.tmatesoft.hg.repo.HgChangelog; @@ -48,7 +48,7 @@ dump.complete = true; //cmdLineOpts; dump.verbose = false; //cmdLineOpts; dump.reverseOrder = true; - LogCommand cmd = new LogCommand(hgRepo); + HgLogCommand cmd = new HgLogCommand(hgRepo); if (cmdLineOpts.users != null) { for (String u : cmdLineOpts.users) { cmd.user(u); @@ -115,7 +115,7 @@ return rv; } - private static final class Dump implements LogCommand.FileHistoryHandler { + private static final class Dump implements HgLogCommand.FileHistoryHandler { // params boolean complete = false; // roughly --debug boolean reverseOrder = false;
--- a/cmdline/org/tmatesoft/hg/console/Manifest.java Wed Feb 16 20:33:31 2011 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Manifest.java Wed Feb 16 20:47:56 2011 +0100 @@ -18,10 +18,10 @@ import static org.tmatesoft.hg.repo.HgRepository.TIP; -import org.tmatesoft.hg.core.LogCommand.FileRevision; +import org.tmatesoft.hg.core.HgLogCommand.FileRevision; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.core.Path; -import org.tmatesoft.hg.core.RepositoryTreeWalker; +import org.tmatesoft.hg.core.HgManifestCommand; import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.repo.HgRepository; @@ -43,7 +43,7 @@ System.out.println(hgRepo.getLocation()); hgRepo.getManifest().walk(0, TIP, new Dump()); // - new RepositoryTreeWalker(hgRepo).dirs(true).walk(new RepositoryTreeWalker.Handler() { + new HgManifestCommand(hgRepo).dirs(true).walk(new HgManifestCommand.Handler() { public void begin(Nodeid manifestRevision) { System.out.println(">> " + manifestRevision);
--- a/design.txt Wed Feb 16 20:33:31 2011 +0100 +++ b/design.txt Wed Feb 16 20:47:56 2011 +0100 @@ -81,7 +81,12 @@ - CommandContext - Data access - not bytes, but ByteChannel - HgRepository constants (TIP, BAD, WC) to HgRevisions enum - - RevisionMap to replace TreeMap<Integer, ?> + - RevisionMap to replace TreeMap<Integer, ?> + + .core.* rename to Hg* + + RepositoryTreeWalker to ManifestCommand to match other command classes + +* defects + - ConfigFile to strip comments from values (#) <<<<<
--- a/src/org/tmatesoft/hg/core/CatCommand.java Wed Feb 16 20:33:31 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -/* - * 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@hg4j.com - */ -package org.tmatesoft.hg.core; - -import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; -import static org.tmatesoft.hg.repo.HgRepository.TIP; - -import java.io.FileNotFoundException; - -import org.tmatesoft.hg.repo.HgDataFile; -import org.tmatesoft.hg.repo.HgRepository; -import org.tmatesoft.hg.util.ByteChannel; - -/** - * Command to obtain content of a - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -public class CatCommand { - - private final HgRepository repo; - private Path file; - private int localRevision = TIP; - private Nodeid revision; - - public CatCommand(HgRepository hgRepo) { - repo = hgRepo; - } - - public CatCommand file(Path fname) { - file = fname; - return this; - } - - // rev can't be WORKING_COPY (if allowed, need to implement in #execute()) - public CatCommand revision(int rev) { - localRevision = rev; - revision = null; - return this; - } - - public CatCommand revision(Nodeid nodeid) { - revision = nodeid; - localRevision = BAD_REVISION; - return this; - } - - public void execute(ByteChannel sink) throws Exception /*TODO own exception type*/ { - if (localRevision == BAD_REVISION && revision == null) { - throw new IllegalArgumentException("Either local file revision number or nodeid shall be specified"); - } - if (file == null) { - throw new IllegalArgumentException("Name of the file is missing"); - } - if (sink == null) { - throw new IllegalArgumentException(); - } - HgDataFile dataFile = repo.getFileNode(file); - if (!dataFile.exists()) { - throw new FileNotFoundException(); - } - int revToExtract; - if (revision != null) { - revToExtract = dataFile.getLocalRevision(revision); - } else { - revToExtract = localRevision; - } - dataFile.content(revToExtract, sink, true); - } -}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgCatCommand.java Wed Feb 16 20:47:56 2011 +0100 @@ -0,0 +1,85 @@ +/* + * 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@hg4j.com + */ +package org.tmatesoft.hg.core; + +import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; +import static org.tmatesoft.hg.repo.HgRepository.TIP; + +import java.io.FileNotFoundException; + +import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.util.ByteChannel; + +/** + * Command to obtain content of a file, 'hg cat' counterpart. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class HgCatCommand { + + private final HgRepository repo; + private Path file; + private int localRevision = TIP; + private Nodeid revision; + + public HgCatCommand(HgRepository hgRepo) { + repo = hgRepo; + } + + public HgCatCommand file(Path fname) { + file = fname; + return this; + } + + // rev can't be WORKING_COPY (if allowed, need to implement in #execute()) + public HgCatCommand revision(int rev) { + localRevision = rev; + revision = null; + return this; + } + + public HgCatCommand revision(Nodeid nodeid) { + revision = nodeid; + localRevision = BAD_REVISION; + return this; + } + + public void execute(ByteChannel sink) throws Exception /*TODO own exception type*/ { + if (localRevision == BAD_REVISION && revision == null) { + throw new IllegalArgumentException("Either local file revision number or nodeid shall be specified"); + } + if (file == null) { + throw new IllegalArgumentException("Name of the file is missing"); + } + if (sink == null) { + throw new IllegalArgumentException(); + } + HgDataFile dataFile = repo.getFileNode(file); + if (!dataFile.exists()) { + throw new FileNotFoundException(); + } + int revToExtract; + if (revision != null) { + revToExtract = dataFile.getLocalRevision(revision); + } else { + revToExtract = localRevision; + } + dataFile.content(revToExtract, sink, true); + } +}
--- a/src/org/tmatesoft/hg/core/HgChangeset.java Wed Feb 16 20:33:31 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgChangeset.java Wed Feb 16 20:47:56 2011 +0100 @@ -20,7 +20,7 @@ import java.util.Collections; import java.util.List; -import org.tmatesoft.hg.core.LogCommand.FileRevision; +import org.tmatesoft.hg.core.HgLogCommand.FileRevision; import org.tmatesoft.hg.repo.HgChangelog.Changeset; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgStatusCollector; @@ -28,7 +28,8 @@ /** - * TODO rename to Changeset along with original Changeset moved to .repo and renamed to HgChangeset? + * Record in the Mercurial changelog, describing single commit. + * * Not thread-safe, don't try to read from different threads * * @author Artem Tikhomirov
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgLogCommand.java Wed Feb 16 20:47:56 2011 +0100 @@ -0,0 +1,298 @@ +/* + * 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@hg4j.com + */ +package org.tmatesoft.hg.core; + +import static org.tmatesoft.hg.repo.HgRepository.TIP; + +import java.util.Calendar; +import java.util.Collections; +import java.util.ConcurrentModificationException; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import org.tmatesoft.hg.repo.HgChangelog.Changeset; +import org.tmatesoft.hg.repo.HgChangelog; +import org.tmatesoft.hg.repo.HgDataFile; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgStatusCollector; +import org.tmatesoft.hg.util.PathPool; + + +/** + * Access to changelog, 'hg log' command counterpart. + * + * <pre> + * Usage: + * new LogCommand().limit(20).branch("maintenance-2.1").user("me").execute(new MyHandler()); + * </pre> + * Not thread-safe (each thread has to use own {@link HgLogCommand} instance). + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class HgLogCommand implements HgChangelog.Inspector { + + private final HgRepository repo; + private Set<String> users; + private Set<String> branches; + private int limit = 0, count = 0; + private int startRev = 0, endRev = TIP; + private Handler delegate; + private Calendar date; + private Path file; + private boolean followHistory; // makes sense only when file != null + private HgChangeset changeset; + + public HgLogCommand(HgRepository hgRepo) { + repo = hgRepo; + } + + /** + * Limit search to specified user. Multiple user names may be specified. + * @param user - full or partial name of the user, case-insensitive, non-null. + * @return <code>this</code> instance for convenience + */ + public HgLogCommand user(String user) { + if (user == null) { + throw new IllegalArgumentException(); + } + if (users == null) { + users = new TreeSet<String>(); + } + users.add(user.toLowerCase()); + return this; + } + + /** + * Limit search to specified branch. Multiple branch specification possible (changeset from any of these + * would be included in result). If unspecified, all branches are considered. + * @param branch - branch name, case-sensitive, non-null. + * @return <code>this</code> instance for convenience + */ + public HgLogCommand branch(String branch) { + if (branch == null) { + throw new IllegalArgumentException(); + } + if (branches == null) { + branches = new TreeSet<String>(); + } + branches.add(branch); + return this; + } + + // limit search to specific date + // multiple? + public HgLogCommand date(Calendar date) { + this.date = date; + // FIXME implement + // isSet(field) - false => don't use in detection of 'same date' + throw HgRepository.notImplemented(); + } + + /** + * + * @param num - number of changeset to produce. Pass 0 to clear the limit. + * @return <code>this</code> instance for convenience + */ + public HgLogCommand limit(int num) { + limit = num; + return this; + } + + /** + * Limit to specified subset of Changelog, [min(rev1,rev2), max(rev1,rev2)], inclusive. + * Revision may be specified with {@link HgRepository#TIP} + * @param rev1 + * @param rev2 + * @return <code>this</code> instance for convenience + */ + public HgLogCommand range(int rev1, int rev2) { + if (rev1 != TIP && rev2 != TIP) { + startRev = rev2 < rev1 ? rev2 : rev1; + endRev = startRev == rev2 ? rev1 : rev2; + } else if (rev1 == TIP && rev2 != TIP) { + startRev = rev2; + endRev = rev1; + } else { + startRev = rev1; + endRev = rev2; + } + return this; + } + + /** + * Visit history of a given file only. + * @param file path relative to repository root. Pass <code>null</code> to reset. + * @param followCopyRename true to report changesets of the original file(-s), if copy/rename ever occured to the file. + */ + public HgLogCommand file(Path file, boolean followCopyRename) { + // multiple? Bad idea, would need to include extra method into Handler to tell start of next file + this.file = file; + followHistory = followCopyRename; + return this; + } + + /** + * Similar to {@link #execute(org.tmatesoft.hg.repo.Changeset.Inspector)}, collects and return result as a list. + */ + public List<HgChangeset> execute() { + CollectHandler collector = new CollectHandler(); + execute(collector); + return collector.getChanges(); + } + + /** + * + * @param inspector + * @throws IllegalArgumentException when inspector argument is null + * @throws ConcurrentModificationException if this log command instance is already running + */ + public void execute(Handler handler) { + if (handler == null) { + throw new IllegalArgumentException(); + } + if (delegate != null) { + throw new ConcurrentModificationException(); + } + try { + delegate = handler; + count = 0; + changeset = new HgChangeset(new HgStatusCollector(repo), new PathPool(repo.getPathHelper())); + if (file == null) { + repo.getChangelog().range(startRev, endRev, this); + } else { + HgDataFile fileNode = repo.getFileNode(file); + fileNode.history(startRev, endRev, this); + if (fileNode.isCopy()) { + // even if we do not follow history, report file rename + do { + if (handler instanceof FileHistoryHandler) { + FileRevision src = new FileRevision(repo, fileNode.getCopySourceRevision(), fileNode.getCopySourceName()); + FileRevision dst = new FileRevision(repo, fileNode.getRevision(0), fileNode.getPath()); + ((FileHistoryHandler) handler).copy(src, dst); + } + if (limit > 0 && count >= limit) { + // if limit reach, follow is useless. + break; + } + if (followHistory) { + fileNode = repo.getFileNode(fileNode.getCopySourceName()); + fileNode.history(this); + } + } while (followHistory && fileNode.isCopy()); + } + } + } finally { + delegate = null; + changeset = null; + } + } + + // + + public void next(int revisionNumber, Nodeid nodeid, Changeset cset) { + if (limit > 0 && count >= limit) { + return; + } + if (branches != null && !branches.contains(cset.branch())) { + return; + } + if (users != null) { + String csetUser = cset.user().toLowerCase(); + boolean found = false; + for (String u : users) { + if (csetUser.indexOf(u) != -1) { + found = true; + break; + } + } + if (!found) { + return; + } + } + if (date != null) { + // FIXME + } + count++; + changeset.init(revisionNumber, nodeid, cset); + delegate.next(changeset); + } + + public interface Handler { + /** + * @param changeset not necessarily a distinct instance each time, {@link HgChangeset#clone() clone()} if need a copy. + */ + void next(HgChangeset changeset); + } + + /** + * When {@link HgLogCommand} is executed against file, handler passed to {@link HgLogCommand#execute(Handler)} may optionally + * implement this interface to get information about file renames. Method {@link #copy(FileRevision, FileRevision)} would + * get invoked prior any changeset of the original file (if file history being followed) is reported via {@link #next(HgChangeset)}. + * + * For {@link HgLogCommand#file(Path, boolean)} with renamed file path and follow argument set to false, + * {@link #copy(FileRevision, FileRevision)} would be invoked for the first copy/rename in the history of the file, but not + * followed by any changesets. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ + public interface FileHistoryHandler extends Handler { + // XXX perhaps, should distinguish copy from rename? And what about merged revisions and following them? + void copy(FileRevision from, FileRevision to); + } + + public static class CollectHandler implements Handler { + private final List<HgChangeset> result = new LinkedList<HgChangeset>(); + + public List<HgChangeset> getChanges() { + return Collections.unmodifiableList(result); + } + + public void next(HgChangeset changeset) { + result.add(changeset.clone()); + } + } + + public static final class FileRevision { + private final HgRepository repo; + private final Nodeid revision; + private final Path path; + + /*package-local*/FileRevision(HgRepository hgRepo, Nodeid rev, Path p) { + if (hgRepo == null || rev == null || p == null) { + throw new IllegalArgumentException(); + } + repo = hgRepo; + revision = rev; + path = p; + } + + public Path getPath() { + return path; + } + public Nodeid getRevision() { + return revision; + } + public byte[] getContent() { + // XXX Content wrapper, to allow formats other than byte[], e.g. Stream, DataAccess, etc? + return repo.getFileNode(path).content(revision); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgManifestCommand.java Wed Feb 16 20:47:56 2011 +0100 @@ -0,0 +1,167 @@ +/* + * 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@hg4j.com + */ +package org.tmatesoft.hg.core; + +import static org.tmatesoft.hg.repo.HgRepository.TIP; + +import java.util.ConcurrentModificationException; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; + +import org.tmatesoft.hg.core.HgLogCommand.FileRevision; +import org.tmatesoft.hg.repo.HgManifest; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.util.PathPool; + + +/** + * Gives access to list of files in each revision (Mercurial manifest information), 'hg manifest' counterpart. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class HgManifestCommand { + + 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 HgManifestCommand(HgRepository hgRepo) { + repo = hgRepo; + } + + public HgManifestCommand range(int rev1, int rev2) { + // if manifest range is different from that of changelog, need conversion utils (external?) + throw HgRepository.notImplemented(); + } + + public HgManifestCommand revision(int rev) { + startRev = endRev = rev; + return this; + } + + public HgManifestCommand 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 HgManifestCommand 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 { + mediator.done(); + visitor = null; + } + } + + /** + * 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; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgRepoFacade.java Wed Feb 16 20:47:56 2011 +0100 @@ -0,0 +1,68 @@ +/* + * 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@hg4j.com + */ +package org.tmatesoft.hg.core; + +import java.io.File; + +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgLookup; + +/** + * Starting point for the library. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class HgRepoFacade { + private HgRepository repo; + + public HgRepoFacade() { + } + + public boolean init() throws Exception /*FIXME RepoInitException*/ { + repo = new HgLookup().detectFromWorkingDir(); + return repo != null && !repo.isInvalid(); + } + + public boolean initFrom(File repoLocation) throws Exception { + repo = new HgLookup().detect(repoLocation.getCanonicalPath()); + return repo != null && !repo.isInvalid(); + } + + public HgRepository getRepository() { + if (repo == null) { + throw new IllegalStateException("Call any of #init*() methods first first"); + } + return repo; + } + + public HgLogCommand createLogCommand() { + return new HgLogCommand(repo/*, getCommandContext()*/); + } + + public HgStatusCommand createStatusCommand() { + return new HgStatusCommand(repo/*, getCommandContext()*/); + } + + public HgCatCommand createCatCommand() { + return new HgCatCommand(repo); + } + + public HgManifestCommand createManifestCommand() { + return new HgManifestCommand(repo); + } +}
--- a/src/org/tmatesoft/hg/core/HgStatus.java Wed Feb 16 20:33:31 2011 +0100 +++ b/src/org/tmatesoft/hg/core/HgStatus.java Wed Feb 16 20:47:56 2011 +0100 @@ -21,6 +21,12 @@ import org.tmatesoft.hg.internal.ChangelogHelper; import org.tmatesoft.hg.repo.HgChangelog.Changeset; +/** + * Repository file status and extra handy information. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ public class HgStatus { public enum Kind {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/core/HgStatusCommand.java Wed Feb 16 20:47:56 2011 +0100 @@ -0,0 +1,265 @@ +/* + * 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@hg4j.com + */ +package org.tmatesoft.hg.core; + +import static org.tmatesoft.hg.core.HgStatus.Kind.*; +import static org.tmatesoft.hg.repo.HgRepository.*; + +import java.util.ConcurrentModificationException; + +import org.tmatesoft.hg.core.Path.Matcher; +import org.tmatesoft.hg.internal.ChangelogHelper; +import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgStatusCollector; +import org.tmatesoft.hg.repo.HgStatusInspector; +import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; + +/** + * Command to obtain file status information, 'hg status' counterpart. + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public class HgStatusCommand { + private final HgRepository repo; + + private int startRevision = TIP; + private int endRevision = WORKING_COPY; + private boolean visitSubRepo = true; + + private final Mediator mediator = new Mediator(); + + public HgStatusCommand(HgRepository hgRepo) { + repo = hgRepo; + defaults(); + } + + public HgStatusCommand defaults() { + final Mediator m = mediator; + m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true; + m.needCopies = m.needClean = m.needIgnored = false; + return this; + } + public HgStatusCommand all() { + final Mediator m = mediator; + m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true; + m.needCopies = m.needClean = m.needIgnored = true; + return this; + } + + + public HgStatusCommand modified(boolean include) { + mediator.needModified = include; + return this; + } + public HgStatusCommand added(boolean include) { + mediator.needAdded = include; + return this; + } + public HgStatusCommand removed(boolean include) { + mediator.needRemoved = include; + return this; + } + public HgStatusCommand deleted(boolean include) { + mediator.needMissing = include; + return this; + } + public HgStatusCommand unknown(boolean include) { + mediator.needUnknown = include; + return this; + } + public HgStatusCommand clean(boolean include) { + mediator.needClean = include; + return this; + } + public HgStatusCommand ignored(boolean include) { + mediator.needIgnored = include; + return this; + } + + /** + * if set, either base:revision or base:workingdir + * to unset, pass {@link HgRepository#TIP} or {@link HgRepository#BAD_REVISION} + * @param revision + * @return + */ + + public HgStatusCommand base(int revision) { + if (revision == WORKING_COPY) { + throw new IllegalArgumentException(); + } + if (revision == BAD_REVISION) { + revision = TIP; + } + startRevision = revision; + return this; + } + + /** + * Revision without base == --change + * Pass {@link HgRepository#WORKING_COPY} or {@link HgRepository#BAD_REVISION} to reset + * @param revision + * @return + */ + public HgStatusCommand revision(int revision) { + if (revision == BAD_REVISION) { + revision = WORKING_COPY; + } + // XXX negative values, except for predefined constants, shall throw IAE. + endRevision = revision; + return this; + } + + // pass null to reset + public HgStatusCommand match(Path.Matcher pathMatcher) { + mediator.matcher = pathMatcher; + return this; + } + + public HgStatusCommand subrepo(boolean visit) { + visitSubRepo = visit; + throw HgRepository.notImplemented(); + } + + /** + * Perform status operation according to parameters set. + * + * @param handler callback to get status information + * @throws IllegalArgumentException if handler is <code>null</code> + * @throws ConcurrentModificationException if this command already runs (i.e. being used from another thread) + */ + public void execute(Handler statusHandler) { + if (statusHandler == null) { + throw new IllegalArgumentException(); + } + if (mediator.busy()) { + throw new ConcurrentModificationException(); + } + HgStatusCollector sc = new HgStatusCollector(repo); // TODO from CommandContext +// PathPool pathHelper = new PathPool(repo.getPathHelper()); // TODO from CommandContext + try { + // XXX if I need a rough estimation (for ProgressMonitor) of number of work units, + // I may use number of files in either rev1 or rev2 manifest edition + mediator.start(statusHandler, new ChangelogHelper(repo, startRevision)); + if (endRevision == WORKING_COPY) { + HgWorkingCopyStatusCollector wcsc = new HgWorkingCopyStatusCollector(repo); + wcsc.setBaseRevisionCollector(sc); + wcsc.walk(startRevision, mediator); + } else { + if (startRevision == TIP) { + sc.change(endRevision, mediator); + } else { + sc.walk(startRevision, endRevision, mediator); + } + } + } finally { + mediator.done(); + } + } + + public interface Handler { + void handleStatus(HgStatus s); + } + + private class Mediator implements HgStatusInspector { + boolean needModified; + boolean needAdded; + boolean needRemoved; + boolean needUnknown; + boolean needMissing; + boolean needClean; + boolean needIgnored; + boolean needCopies; + Matcher matcher; + Handler handler; + private ChangelogHelper logHelper; + + Mediator() { + } + + public void start(Handler h, ChangelogHelper changelogHelper) { + handler = h; + logHelper = changelogHelper; + } + + public void done() { + handler = null; + logHelper = null; + } + + public boolean busy() { + return handler != null; + } + + public void modified(Path fname) { + if (needModified) { + if (matcher == null || matcher.accept(fname)) { + handler.handleStatus(new HgStatus(Modified, fname, logHelper)); + } + } + } + public void added(Path fname) { + if (needAdded) { + if (matcher == null || matcher.accept(fname)) { + handler.handleStatus(new HgStatus(Added, fname, logHelper)); + } + } + } + public void removed(Path fname) { + if (needRemoved) { + if (matcher == null || matcher.accept(fname)) { + handler.handleStatus(new HgStatus(Removed, fname, logHelper)); + } + } + } + public void copied(Path fnameOrigin, Path fnameAdded) { + if (needCopies) { + if (matcher == null || matcher.accept(fnameAdded)) { + handler.handleStatus(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper)); + } + } + } + public void missing(Path fname) { + if (needMissing) { + if (matcher == null || matcher.accept(fname)) { + handler.handleStatus(new HgStatus(Missing, fname, logHelper)); + } + } + } + public void unknown(Path fname) { + if (needUnknown) { + if (matcher == null || matcher.accept(fname)) { + handler.handleStatus(new HgStatus(Unknown, fname, logHelper)); + } + } + } + public void clean(Path fname) { + if (needClean) { + if (matcher == null || matcher.accept(fname)) { + handler.handleStatus(new HgStatus(Clean, fname, logHelper)); + } + } + } + public void ignored(Path fname) { + if (needIgnored) { + if (matcher == null || matcher.accept(fname)) { + handler.handleStatus(new HgStatus(Ignored, fname, logHelper)); + } + } + } + } +}
--- a/src/org/tmatesoft/hg/core/LogCommand.java Wed Feb 16 20:33:31 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,295 +0,0 @@ -/* - * 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@hg4j.com - */ -package org.tmatesoft.hg.core; - -import static org.tmatesoft.hg.repo.HgRepository.TIP; - -import java.util.Calendar; -import java.util.Collections; -import java.util.ConcurrentModificationException; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -import org.tmatesoft.hg.repo.HgChangelog.Changeset; -import org.tmatesoft.hg.repo.HgChangelog; -import org.tmatesoft.hg.repo.HgDataFile; -import org.tmatesoft.hg.repo.HgRepository; -import org.tmatesoft.hg.repo.HgStatusCollector; -import org.tmatesoft.hg.util.PathPool; - - -/** - * <pre> - * new LogCommand().limit(20).branch("maintenance-2.1").user("me").execute(new MyHandler()); - * </pre> - * Not thread-safe (each thread has to use own {@link LogCommand} instance). - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -public class LogCommand implements HgChangelog.Inspector { - - private final HgRepository repo; - private Set<String> users; - private Set<String> branches; - private int limit = 0, count = 0; - private int startRev = 0, endRev = TIP; - private Handler delegate; - private Calendar date; - private Path file; - private boolean followHistory; // makes sense only when file != null - private HgChangeset changeset; - - public LogCommand(HgRepository hgRepo) { - repo = hgRepo; - } - - /** - * Limit search to specified user. Multiple user names may be specified. - * @param user - full or partial name of the user, case-insensitive, non-null. - * @return <code>this</code> instance for convenience - */ - public LogCommand user(String user) { - if (user == null) { - throw new IllegalArgumentException(); - } - if (users == null) { - users = new TreeSet<String>(); - } - users.add(user.toLowerCase()); - return this; - } - - /** - * Limit search to specified branch. Multiple branch specification possible (changeset from any of these - * would be included in result). If unspecified, all branches are considered. - * @param branch - branch name, case-sensitive, non-null. - * @return <code>this</code> instance for convenience - */ - public LogCommand branch(String branch) { - if (branch == null) { - throw new IllegalArgumentException(); - } - if (branches == null) { - branches = new TreeSet<String>(); - } - branches.add(branch); - return this; - } - - // limit search to specific date - // multiple? - public LogCommand date(Calendar date) { - this.date = date; - // FIXME implement - // isSet(field) - false => don't use in detection of 'same date' - throw HgRepository.notImplemented(); - } - - /** - * - * @param num - number of changeset to produce. Pass 0 to clear the limit. - * @return <code>this</code> instance for convenience - */ - public LogCommand limit(int num) { - limit = num; - return this; - } - - /** - * Limit to specified subset of Changelog, [min(rev1,rev2), max(rev1,rev2)], inclusive. - * Revision may be specified with {@link HgRepository#TIP} - * @param rev1 - * @param rev2 - * @return <code>this</code> instance for convenience - */ - public LogCommand range(int rev1, int rev2) { - if (rev1 != TIP && rev2 != TIP) { - startRev = rev2 < rev1 ? rev2 : rev1; - endRev = startRev == rev2 ? rev1 : rev2; - } else if (rev1 == TIP && rev2 != TIP) { - startRev = rev2; - endRev = rev1; - } else { - startRev = rev1; - endRev = rev2; - } - return this; - } - - /** - * Visit history of a given file only. - * @param file path relative to repository root. Pass <code>null</code> to reset. - * @param followCopyRename true to report changesets of the original file(-s), if copy/rename ever occured to the file. - */ - public LogCommand file(Path file, boolean followCopyRename) { - // multiple? Bad idea, would need to include extra method into Handler to tell start of next file - this.file = file; - followHistory = followCopyRename; - return this; - } - - /** - * Similar to {@link #execute(org.tmatesoft.hg.repo.Changeset.Inspector)}, collects and return result as a list. - */ - public List<HgChangeset> execute() { - CollectHandler collector = new CollectHandler(); - execute(collector); - return collector.getChanges(); - } - - /** - * - * @param inspector - * @throws IllegalArgumentException when inspector argument is null - * @throws ConcurrentModificationException if this log command instance is already running - */ - public void execute(Handler handler) { - if (handler == null) { - throw new IllegalArgumentException(); - } - if (delegate != null) { - throw new ConcurrentModificationException(); - } - try { - delegate = handler; - count = 0; - changeset = new HgChangeset(new HgStatusCollector(repo), new PathPool(repo.getPathHelper())); - if (file == null) { - repo.getChangelog().range(startRev, endRev, this); - } else { - HgDataFile fileNode = repo.getFileNode(file); - fileNode.history(startRev, endRev, this); - if (fileNode.isCopy()) { - // even if we do not follow history, report file rename - do { - if (handler instanceof FileHistoryHandler) { - FileRevision src = new FileRevision(repo, fileNode.getCopySourceRevision(), fileNode.getCopySourceName()); - FileRevision dst = new FileRevision(repo, fileNode.getRevision(0), fileNode.getPath()); - ((FileHistoryHandler) handler).copy(src, dst); - } - if (limit > 0 && count >= limit) { - // if limit reach, follow is useless. - break; - } - if (followHistory) { - fileNode = repo.getFileNode(fileNode.getCopySourceName()); - fileNode.history(this); - } - } while (followHistory && fileNode.isCopy()); - } - } - } finally { - delegate = null; - changeset = null; - } - } - - // - - public void next(int revisionNumber, Nodeid nodeid, Changeset cset) { - if (limit > 0 && count >= limit) { - return; - } - if (branches != null && !branches.contains(cset.branch())) { - return; - } - if (users != null) { - String csetUser = cset.user().toLowerCase(); - boolean found = false; - for (String u : users) { - if (csetUser.indexOf(u) != -1) { - found = true; - break; - } - } - if (!found) { - return; - } - } - if (date != null) { - // FIXME - } - count++; - changeset.init(revisionNumber, nodeid, cset); - delegate.next(changeset); - } - - public interface Handler { - /** - * @param changeset not necessarily a distinct instance each time, {@link HgChangeset#clone() clone()} if need a copy. - */ - void next(HgChangeset changeset); - } - - /** - * When {@link LogCommand} is executed against file, handler passed to {@link LogCommand#execute(Handler)} may optionally - * implement this interface to get information about file renames. Method {@link #copy(FileRevision, FileRevision)} would - * get invoked prior any changeset of the original file (if file history being followed) is reported via {@link #next(HgChangeset)}. - * - * For {@link LogCommand#file(Path, boolean)} with renamed file path and follow argument set to false, - * {@link #copy(FileRevision, FileRevision)} would be invoked for the first copy/rename in the history of the file, but not - * followed by any changesets. - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ - public interface FileHistoryHandler extends Handler { - // XXX perhaps, should distinguish copy from rename? And what about merged revisions and following them? - void copy(FileRevision from, FileRevision to); - } - - public static class CollectHandler implements Handler { - private final List<HgChangeset> result = new LinkedList<HgChangeset>(); - - public List<HgChangeset> getChanges() { - return Collections.unmodifiableList(result); - } - - public void next(HgChangeset changeset) { - result.add(changeset.clone()); - } - } - - public static final class FileRevision { - private final HgRepository repo; - private final Nodeid revision; - private final Path path; - - /*package-local*/FileRevision(HgRepository hgRepo, Nodeid rev, Path p) { - if (hgRepo == null || rev == null || p == null) { - throw new IllegalArgumentException(); - } - repo = hgRepo; - revision = rev; - path = p; - } - - public Path getPath() { - return path; - } - public Nodeid getRevision() { - return revision; - } - public byte[] getContent() { - // XXX Content wrapper, to allow formats other than byte[], e.g. Stream, DataAccess, etc? - return repo.getFileNode(path).content(revision); - } - } -}
--- a/src/org/tmatesoft/hg/core/RepositoryFacade.java Wed Feb 16 20:33:31 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* - * 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@hg4j.com - */ -package org.tmatesoft.hg.core; - -import java.io.File; - -import org.tmatesoft.hg.repo.HgRepository; -import org.tmatesoft.hg.repo.HgLookup; - -/** - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -public class RepositoryFacade { - private HgRepository repo; - - public RepositoryFacade() { - } - - public boolean init() throws Exception /*FIXME RepoInitException*/ { - repo = new HgLookup().detectFromWorkingDir(); - return repo != null && !repo.isInvalid(); - } - - public boolean initFrom(File repoLocation) throws Exception { - repo = new HgLookup().detect(repoLocation.getCanonicalPath()); - return repo != null && !repo.isInvalid(); - } - - public HgRepository getRepository() { - if (repo == null) { - throw new IllegalStateException("Call any of #init*() methods first first"); - } - return repo; - } - - public LogCommand createLogCommand() { - return new LogCommand(repo/*, getCommandContext()*/); - } - - public StatusCommand createStatusCommand() { - return new StatusCommand(repo/*, getCommandContext()*/); - } - - public CatCommand createCatCommand() { - return new CatCommand(repo); - } - - public RepositoryTreeWalker createManifestCommand() { - return new RepositoryTreeWalker(repo); - } -}
--- a/src/org/tmatesoft/hg/core/RepositoryTreeWalker.java Wed Feb 16 20:33:31 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,166 +0,0 @@ -/* - * 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@hg4j.com - */ -package org.tmatesoft.hg.core; - -import static org.tmatesoft.hg.repo.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.repo.HgManifest; -import org.tmatesoft.hg.repo.HgRepository; -import org.tmatesoft.hg.util.PathPool; - - -/** - * - * @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) { - 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 revision(int rev) { - startRev = endRev = rev; - return this; - } - - 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 { - mediator.done(); - visitor = null; - } - } - - /** - * 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; - } - } -}
--- a/src/org/tmatesoft/hg/core/StatusCommand.java Wed Feb 16 20:33:31 2011 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +0,0 @@ -/* - * 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@hg4j.com - */ -package org.tmatesoft.hg.core; - -import static org.tmatesoft.hg.core.HgStatus.Kind.*; -import static org.tmatesoft.hg.repo.HgRepository.*; - -import java.util.ConcurrentModificationException; - -import org.tmatesoft.hg.core.Path.Matcher; -import org.tmatesoft.hg.internal.ChangelogHelper; -import org.tmatesoft.hg.repo.HgRepository; -import org.tmatesoft.hg.repo.HgStatusCollector; -import org.tmatesoft.hg.repo.HgStatusInspector; -import org.tmatesoft.hg.repo.HgWorkingCopyStatusCollector; - -/** - * - * @author Artem Tikhomirov - * @author TMate Software Ltd. - */ -public class StatusCommand { - private final HgRepository repo; - - private int startRevision = TIP; - private int endRevision = WORKING_COPY; - private boolean visitSubRepo = true; - - private final Mediator mediator = new Mediator(); - - public StatusCommand(HgRepository hgRepo) { - repo = hgRepo; - defaults(); - } - - public StatusCommand defaults() { - final Mediator m = mediator; - m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true; - m.needCopies = m.needClean = m.needIgnored = false; - return this; - } - public StatusCommand all() { - final Mediator m = mediator; - m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true; - m.needCopies = m.needClean = m.needIgnored = true; - return this; - } - - - public StatusCommand modified(boolean include) { - mediator.needModified = include; - return this; - } - public StatusCommand added(boolean include) { - mediator.needAdded = include; - return this; - } - public StatusCommand removed(boolean include) { - mediator.needRemoved = include; - return this; - } - public StatusCommand deleted(boolean include) { - mediator.needMissing = include; - return this; - } - public StatusCommand unknown(boolean include) { - mediator.needUnknown = include; - return this; - } - public StatusCommand clean(boolean include) { - mediator.needClean = include; - return this; - } - public StatusCommand ignored(boolean include) { - mediator.needIgnored = include; - return this; - } - - /** - * if set, either base:revision or base:workingdir - * to unset, pass {@link HgRepository#TIP} or {@link HgRepository#BAD_REVISION} - * @param revision - * @return - */ - - public StatusCommand base(int revision) { - if (revision == WORKING_COPY) { - throw new IllegalArgumentException(); - } - if (revision == BAD_REVISION) { - revision = TIP; - } - startRevision = revision; - return this; - } - - /** - * Revision without base == --change - * Pass {@link HgRepository#WORKING_COPY} or {@link HgRepository#BAD_REVISION} to reset - * @param revision - * @return - */ - public StatusCommand revision(int revision) { - if (revision == BAD_REVISION) { - revision = WORKING_COPY; - } - // XXX negative values, except for predefined constants, shall throw IAE. - endRevision = revision; - return this; - } - - // pass null to reset - public StatusCommand match(Path.Matcher pathMatcher) { - mediator.matcher = pathMatcher; - return this; - } - - public StatusCommand subrepo(boolean visit) { - visitSubRepo = visit; - throw HgRepository.notImplemented(); - } - - /** - * Perform status operation according to parameters set. - * - * @param handler callback to get status information - * @throws IllegalArgumentException if handler is <code>null</code> - * @throws ConcurrentModificationException if this command already runs (i.e. being used from another thread) - */ - public void execute(Handler statusHandler) { - if (statusHandler == null) { - throw new IllegalArgumentException(); - } - if (mediator.busy()) { - throw new ConcurrentModificationException(); - } - HgStatusCollector sc = new HgStatusCollector(repo); // TODO from CommandContext -// PathPool pathHelper = new PathPool(repo.getPathHelper()); // TODO from CommandContext - try { - // XXX if I need a rough estimation (for ProgressMonitor) of number of work units, - // I may use number of files in either rev1 or rev2 manifest edition - mediator.start(statusHandler, new ChangelogHelper(repo, startRevision)); - if (endRevision == WORKING_COPY) { - HgWorkingCopyStatusCollector wcsc = new HgWorkingCopyStatusCollector(repo); - wcsc.setBaseRevisionCollector(sc); - wcsc.walk(startRevision, mediator); - } else { - if (startRevision == TIP) { - sc.change(endRevision, mediator); - } else { - sc.walk(startRevision, endRevision, mediator); - } - } - } finally { - mediator.done(); - } - } - - public interface Handler { - void handleStatus(HgStatus s); - } - - private class Mediator implements HgStatusInspector { - boolean needModified; - boolean needAdded; - boolean needRemoved; - boolean needUnknown; - boolean needMissing; - boolean needClean; - boolean needIgnored; - boolean needCopies; - Matcher matcher; - Handler handler; - private ChangelogHelper logHelper; - - Mediator() { - } - - public void start(Handler h, ChangelogHelper changelogHelper) { - handler = h; - logHelper = changelogHelper; - } - - public void done() { - handler = null; - logHelper = null; - } - - public boolean busy() { - return handler != null; - } - - public void modified(Path fname) { - if (needModified) { - if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Modified, fname, logHelper)); - } - } - } - public void added(Path fname) { - if (needAdded) { - if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Added, fname, logHelper)); - } - } - } - public void removed(Path fname) { - if (needRemoved) { - if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Removed, fname, logHelper)); - } - } - } - public void copied(Path fnameOrigin, Path fnameAdded) { - if (needCopies) { - if (matcher == null || matcher.accept(fnameAdded)) { - handler.handleStatus(new HgStatus(Added, fnameAdded, fnameOrigin, logHelper)); - } - } - } - public void missing(Path fname) { - if (needMissing) { - if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Missing, fname, logHelper)); - } - } - } - public void unknown(Path fname) { - if (needUnknown) { - if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Unknown, fname, logHelper)); - } - } - } - public void clean(Path fname) { - if (needClean) { - if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Clean, fname, logHelper)); - } - } - } - public void ignored(Path fname) { - if (needIgnored) { - if (matcher == null || matcher.accept(fname)) { - handler.handleStatus(new HgStatus(Ignored, fname, logHelper)); - } - } - } - } -}
--- a/test/org/tmatesoft/hg/test/TestByteChannel.java Wed Feb 16 20:33:31 2011 +0100 +++ b/test/org/tmatesoft/hg/test/TestByteChannel.java Wed Feb 16 20:47:56 2011 +0100 @@ -19,7 +19,7 @@ import java.util.Arrays; import org.junit.Assert; -import org.tmatesoft.hg.core.RepositoryFacade; +import org.tmatesoft.hg.core.HgRepoFacade; import org.tmatesoft.hg.internal.ByteArrayChannel; import org.tmatesoft.hg.repo.HgDataFile; @@ -31,7 +31,7 @@ public class TestByteChannel { public static void main(String[] args) throws Exception { - RepositoryFacade rf = new RepositoryFacade(); + HgRepoFacade rf = new HgRepoFacade(); rf.init(); HgDataFile file = rf.getRepository().getFileNode("src/org/tmatesoft/hg/internal/KeywordFilter.java"); for (int i = file.getRevisionCount() - 1; i >= 0; i--) {
--- a/test/org/tmatesoft/hg/test/TestHistory.java Wed Feb 16 20:33:31 2011 +0100 +++ b/test/org/tmatesoft/hg/test/TestHistory.java Wed Feb 16 20:47:56 2011 +0100 @@ -29,10 +29,10 @@ import org.junit.Rule; import org.junit.Test; import org.tmatesoft.hg.core.HgChangeset; -import org.tmatesoft.hg.core.LogCommand; -import org.tmatesoft.hg.core.LogCommand.CollectHandler; -import org.tmatesoft.hg.core.LogCommand.FileHistoryHandler; -import org.tmatesoft.hg.core.LogCommand.FileRevision; +import org.tmatesoft.hg.core.HgLogCommand; +import org.tmatesoft.hg.core.HgLogCommand.CollectHandler; +import org.tmatesoft.hg.core.HgLogCommand.FileHistoryHandler; +import org.tmatesoft.hg.core.HgLogCommand.FileRevision; import org.tmatesoft.hg.core.Path; import org.tmatesoft.hg.repo.HgLookup; import org.tmatesoft.hg.repo.HgRepository; @@ -74,7 +74,7 @@ public void testCompleteLog() throws Exception { changelogParser.reset(); eh.run("hg", "log", "--debug"); - List<HgChangeset> r = new LogCommand(repo).execute(); + List<HgChangeset> r = new HgLogCommand(repo).execute(); report("hg log - COMPLETE REPO HISTORY", r, true); } @@ -95,7 +95,7 @@ } }; H h = new H(); - new LogCommand(repo).file(f, true).execute(h); + new HgLogCommand(repo).file(f, true).execute(h); String what = "hg log - FOLLOW FILE HISTORY"; errorCollector.checkThat(what + "#copyReported ", h.copyReported, is(true)); errorCollector.checkThat(what + "#copyFromMatched", h.fromMatched, is(true)); @@ -144,7 +144,7 @@ } final long start2 = System.currentTimeMillis(); for (int i = 0; i < runs; i++) { - new LogCommand(repo).execute(); + new HgLogCommand(repo).execute(); } final long end = System.currentTimeMillis(); System.out.printf("'hg log --debug', %d runs: Native client total %d (%d per run), Java client %d (%d)\n", runs, start2-start1, (start2-start1)/runs, end-start2, (end-start2)/runs);
--- a/test/org/tmatesoft/hg/test/TestManifest.java Wed Feb 16 20:33:31 2011 +0100 +++ b/test/org/tmatesoft/hg/test/TestManifest.java Wed Feb 16 20:47:56 2011 +0100 @@ -30,10 +30,10 @@ import org.junit.Assume; import org.junit.Rule; import org.junit.Test; -import org.tmatesoft.hg.core.LogCommand.FileRevision; +import org.tmatesoft.hg.core.HgLogCommand.FileRevision; import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.core.Path; -import org.tmatesoft.hg.core.RepositoryTreeWalker; +import org.tmatesoft.hg.core.HgManifestCommand; import org.tmatesoft.hg.repo.HgLookup; import org.tmatesoft.hg.repo.HgRepository; @@ -52,7 +52,7 @@ private ManifestOutputParser manifestParser; private ExecHelper eh; final LinkedList<FileRevision> revisions = new LinkedList<FileRevision>(); - private RepositoryTreeWalker.Handler handler = new RepositoryTreeWalker.Handler() { + private HgManifestCommand.Handler handler = new HgManifestCommand.Handler() { public void file(FileRevision fileRevision) { revisions.add(fileRevision); @@ -104,7 +104,7 @@ manifestParser.reset(); eh.run("hg", "manifest", "--debug", "--rev", String.valueOf(rev)); revisions.clear(); - new RepositoryTreeWalker(repo).revision(rev).walk(handler); + new HgManifestCommand(repo).revision(rev).walk(handler); report("manifest " + (rev == TIP ? "TIP:" : "--rev " + rev)); }
--- a/test/org/tmatesoft/hg/test/TestStatus.java Wed Feb 16 20:33:31 2011 +0100 +++ b/test/org/tmatesoft/hg/test/TestStatus.java Wed Feb 16 20:47:56 2011 +0100 @@ -34,7 +34,7 @@ import org.junit.Test; import org.tmatesoft.hg.core.HgStatus; import org.tmatesoft.hg.core.Path; -import org.tmatesoft.hg.core.StatusCommand; +import org.tmatesoft.hg.core.HgStatusCommand; import org.tmatesoft.hg.repo.HgLookup; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.HgStatusCollector; @@ -104,7 +104,7 @@ @Test public void testStatusCommand() throws Exception { - final StatusCommand sc = new StatusCommand(repo).all(); + final HgStatusCommand sc = new HgStatusCommand(repo).all(); StatusCollector r; statusParser.reset(); eh.run("hg", "status", "-A"); @@ -125,7 +125,7 @@ // TODO check not -A, but defaults()/custom set of modifications } - private static class StatusCollector implements StatusCommand.Handler { + private static class StatusCollector implements HgStatusCommand.Handler { private final Map<HgStatus.Kind, List<Path>> map = new TreeMap<HgStatus.Kind, List<Path>>(); public void handleStatus(HgStatus s) { @@ -170,7 +170,7 @@ final long start2 = System.currentTimeMillis(); for (int i = 0; i < runs; i++) { StatusCollector r = new StatusCollector(); - new StatusCommand(repo).all().base(3).revision(80).execute(r); + new HgStatusCommand(repo).all().base(3).revision(80).execute(r); } final long end = System.currentTimeMillis(); System.out.printf("'hg status -A --rev 3:80', %d runs: Native client total %d (%d per run), Java client %d (%d)\n", runs, start2-start1, (start2-start1)/runs, end-start2, (end-start2)/runs);