# HG changeset patch # User Artem Tikhomirov # Date 1296159321 -3600 # Node ID d55d4eedfc57451c3c4dd5846ff71b249936ee5a # Parent bf304cb14247a9feea2374d0996c4a6be052398b Switch to Path instead of String in filenames returned by various status operations diff -r bf304cb14247 -r d55d4eedfc57 cmdline/org/tmatesoft/hg/console/Status.java --- a/cmdline/org/tmatesoft/hg/console/Status.java Thu Jan 27 06:31:58 2011 +0100 +++ b/cmdline/org/tmatesoft/hg/console/Status.java Thu Jan 27 21:15:21 2011 +0100 @@ -27,6 +27,7 @@ import org.tmatesoft.hg.core.Path; import org.tmatesoft.hg.repo.HgDataFile; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgStatusInspector; import org.tmatesoft.hg.repo.Internals; import org.tmatesoft.hg.repo.StatusCollector; import org.tmatesoft.hg.repo.StatusCollector.Record; @@ -117,13 +118,13 @@ sortAndPrint('!', r.getMissing()); } - private static void sortAndPrint(char prefix, List ul) { + private static void sortAndPrint(char prefix, List ul) { sortAndPrint(prefix, ul, null); } - private static void sortAndPrint(char prefix, List ul, Map copies) { - ArrayList sortList = new ArrayList(ul); + private static void sortAndPrint(char prefix, List ul, Map copies) { + ArrayList sortList = new ArrayList(ul); Collections.sort(sortList); - for (String s : sortList) { + for (Path s : sortList) { System.out.print(prefix); System.out.print(' '); System.out.println(s); @@ -143,52 +144,52 @@ } } - private static class StatusDump implements StatusCollector.Inspector { + private static class StatusDump implements HgStatusInspector { public boolean hideStatusPrefix = false; // hg status -n option public boolean showCopied = true; // -C public boolean showIgnored = true; // -i public boolean showClean = true; // -c - public void modified(String fname) { + public void modified(Path fname) { print('M', fname); } - public void added(String fname) { + public void added(Path fname) { print('A', fname); } - public void copied(String fnameOrigin, String fnameAdded) { + public void copied(Path fnameOrigin, Path fnameAdded) { added(fnameAdded); if (showCopied) { print(' ', fnameOrigin); } } - public void removed(String fname) { + public void removed(Path fname) { print('R', fname); } - public void clean(String fname) { + public void clean(Path fname) { if (showClean) { print('C', fname); } } - public void missing(String fname) { + public void missing(Path fname) { print('!', fname); } - public void unknown(String fname) { + public void unknown(Path fname) { print('?', fname); } - public void ignored(String fname) { + public void ignored(Path fname) { if (showIgnored) { print('I', fname); } } - private void print(char status, String fname) { + private void print(char status, Path fname) { if (!hideStatusPrefix) { System.out.print(status); System.out.print(' '); diff -r bf304cb14247 -r d55d4eedfc57 design.txt --- a/design.txt Thu Jan 27 06:31:58 2011 +0100 +++ b/design.txt Thu Jan 27 21:15:21 2011 +0100 @@ -42,6 +42,7 @@ DataAccess - collect debug info (buffer misses, file size/total read operations) to find out better strategy to buffer size detection. Compare performance. Strip off metadata from beg of the stream - DataAccess (with rebase/moveBaseOffset(int)) would be handy +Parameterize StatusCollector to produce copy only when needed. And HgDataFile.metadata perhaps should be moved to cacheable place? Status operation from GUI - guess, usually on a file/subfolder, hence API should allow for starting path (unlike cmdline, seems useless to implement include/exclide patterns - GUI users hardly enter them, ever) -> recently introduced FileWalker may perhaps help solving this (if starts walking from selected folder) for status op against WorkingDir? @@ -56,7 +57,9 @@ ? hg status, compare revision and local file with kw expansion and eol extension ? subrepos in log, status (-S) and manifest commands + Commands to get CommandContext where they may share various caches (e.g. StatusCollector) +Perhaps, abstract classes for all Inspectors (i.e. StatusCollector.Inspector) for users to use as base classes to protect from change? >>>> Effective file read/data access ReadOperation, Revlog does: repo.getFileSystem().run(this.file, new ReadOperation(), long start=0, long end = -1) diff -r bf304cb14247 -r d55d4eedfc57 src/org/tmatesoft/hg/core/Cset.java --- a/src/org/tmatesoft/hg/core/Cset.java Thu Jan 27 06:31:58 2011 +0100 +++ b/src/org/tmatesoft/hg/core/Cset.java Thu Jan 27 21:15:21 2011 +0100 @@ -135,24 +135,22 @@ StatusCollector.Record r = new StatusCollector.Record(); statusHelper.change(revNumber, r); final HgRepository repo = statusHelper.getRepo(); - for (String s : r.getModified()) { - Path p = pathHelper.path(s); + for (Path s : r.getModified()) { Nodeid nid = r.nodeidAfterChange(s); if (nid == null) { throw new IllegalArgumentException(); } - modified.add(new FileRevision(repo, nid, p)); + modified.add(new FileRevision(repo, nid, s)); } - for (String s : r.getAdded()) { - Path p = pathHelper.path(s); + for (Path s : r.getAdded()) { Nodeid nid = r.nodeidAfterChange(s); if (nid == null) { throw new IllegalArgumentException(); } - added.add(new FileRevision(repo, nid, p)); + added.add(new FileRevision(repo, nid, s)); } - for (String s : r.getRemoved()) { - deleted.add(pathHelper.path(s)); + for (Path s : r.getRemoved()) { + deleted.add(s); } modified.trimToSize(); added.trimToSize(); diff -r bf304cb14247 -r d55d4eedfc57 src/org/tmatesoft/hg/core/StatusCommand.java --- a/src/org/tmatesoft/hg/core/StatusCommand.java Thu Jan 27 06:31:58 2011 +0100 +++ b/src/org/tmatesoft/hg/core/StatusCommand.java Thu Jan 27 21:15:21 2011 +0100 @@ -20,8 +20,12 @@ import static org.tmatesoft.hg.repo.HgRepository.TIP; import static org.tmatesoft.hg.repo.HgRepository.WORKING_COPY; +import java.util.ConcurrentModificationException; + +import org.tmatesoft.hg.core.LogCommand.FileRevision; import org.tmatesoft.hg.core.Path.Matcher; import org.tmatesoft.hg.repo.HgRepository; +import org.tmatesoft.hg.repo.HgStatusInspector; import org.tmatesoft.hg.repo.StatusCollector; import org.tmatesoft.hg.repo.WorkingCopyStatusCollector; @@ -33,17 +37,12 @@ public class StatusCommand { private final HgRepository repo; - private boolean needModified; - private boolean needAdded; - private boolean needRemoved; - private boolean needUnknown; - private boolean needMissing; - private boolean needClean; - private boolean needIgnored; - private Matcher matcher; private int startRevision = TIP; private int endRevision = WORKING_COPY; private boolean visitSubRepo = true; + + private HgStatusInspector visitor; + private final Mediator mediator = new Mediator(); public StatusCommand(HgRepository hgRepo) { repo = hgRepo; @@ -51,43 +50,45 @@ } public StatusCommand defaults() { - needModified = needAdded = needRemoved = needUnknown = needMissing = true; - needClean = needIgnored = false; + final Mediator m = mediator; + m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true; + m.needClean = m.needIgnored = false; return this; } public StatusCommand all() { - needModified = needAdded = needRemoved = needUnknown = needMissing = true; - needClean = needIgnored = true; + final Mediator m = mediator; + m.needModified = m.needAdded = m.needRemoved = m.needUnknown = m.needMissing = true; + m.needClean = m.needIgnored = true; return this; } public StatusCommand modified(boolean include) { - needModified = include; + mediator.needModified = include; return this; } public StatusCommand added(boolean include) { - needAdded = include; + mediator.needAdded = include; return this; } public StatusCommand removed(boolean include) { - needRemoved = include; + mediator.needRemoved = include; return this; } public StatusCommand deleted(boolean include) { - needMissing = include; + mediator.needMissing = include; return this; } public StatusCommand unknown(boolean include) { - needUnknown = include; + mediator.needUnknown = include; return this; } public StatusCommand clean(boolean include) { - needClean = include; + mediator.needClean = include; return this; } public StatusCommand ignored(boolean include) { - needIgnored = include; + mediator.needIgnored = include; return this; } @@ -124,31 +125,130 @@ return this; } + // pass null to reset public StatusCommand match(Path.Matcher pathMatcher) { - matcher = pathMatcher; - return this; + mediator.matcher = pathMatcher; + throw HgRepository.notImplemented(); } public StatusCommand subrepo(boolean visit) { visitSubRepo = visit; throw HgRepository.notImplemented(); } - - public void execute(StatusCollector.Inspector inspector) { + + /** + * Perform status operation according to parameters set. + * + * @param handler callback to get status information + * @throws IllegalArgumentException if handler is null + * @throws ConcurrentModificationException if this command already runs (i.e. being used from another thread) + */ + public void execute(final HgStatusInspector handler) { + if (handler == null) { + throw new IllegalArgumentException(); + } + if (visitor != null) { + throw new ConcurrentModificationException(); + } + visitor = handler; StatusCollector sc = new StatusCollector(repo); // TODO from CommandContext -// StatusCollector.Record r = new StatusCollector.Record(); // XXX use own inspector not to collect entries that - // are not interesting or do not match name - if (endRevision == WORKING_COPY) { - WorkingCopyStatusCollector wcsc = new WorkingCopyStatusCollector(repo); - wcsc.setBaseRevisionCollector(sc); - wcsc.walk(startRevision, inspector); - } else { - if (startRevision == TIP) { - sc.change(endRevision, inspector); +// 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(); + if (endRevision == WORKING_COPY) { + WorkingCopyStatusCollector wcsc = new WorkingCopyStatusCollector(repo); + wcsc.setBaseRevisionCollector(sc); + wcsc.walk(startRevision, mediator); } else { - sc.walk(startRevision, endRevision, inspector); + if (startRevision == TIP) { + sc.change(endRevision, mediator); + } else { + sc.walk(startRevision, endRevision, mediator); + } + } + } finally { + mediator.done(); + visitor = null; + } + } + + private class Mediator implements HgStatusInspector { + boolean needModified; + boolean needAdded; + boolean needRemoved; + boolean needUnknown; + boolean needMissing; + boolean needClean; + boolean needIgnored; + boolean needCopies = false; // FIXME decide if I need such an argument in StatusComment + Matcher matcher; + + Mediator() { + } + + public void start() { + + } + public void done() { + } + + public void modified(Path fname) { + if (needModified) { + if (matcher == null || matcher.accept(fname)) { + visitor.modified(fname); + } } } -// PathPool pathHelper = new PathPool(repo.getPathHelper()); // TODO from CommandContext + public void added(Path fname) { + if (needAdded) { + if (matcher == null || matcher.accept(fname)) { + visitor.added(fname); + } + } + } + public void removed(Path fname) { + if (needRemoved) { + if (matcher == null || matcher.accept(fname)) { + visitor.removed(fname); + } + } + } + public void copied(Path fnameOrigin, Path fnameAdded) { + if (needCopies) { + if (matcher == null || matcher.accept(fnameAdded)) { + visitor.copied(fnameOrigin, fnameAdded); + } + } + } + public void missing(Path fname) { + if (needMissing) { + if (matcher == null || matcher.accept(fname)) { + visitor.missing(fname); + } + } + } + public void unknown(Path fname) { + if (needUnknown) { + if (matcher == null || matcher.accept(fname)) { + visitor.unknown(fname); + } + } + } + public void clean(Path fname) { + if (needClean) { + if (matcher == null || matcher.accept(fname)) { + visitor.clean(fname); + } + } + } + public void ignored(Path fname) { + if (needIgnored) { + if (matcher == null || matcher.accept(fname)) { + visitor.ignored(fname); + } + } + } } } diff -r bf304cb14247 -r d55d4eedfc57 src/org/tmatesoft/hg/repo/HgDirstate.java --- a/src/org/tmatesoft/hg/repo/HgDirstate.java Thu Jan 27 06:31:58 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/HgDirstate.java Thu Jan 27 21:15:21 2011 +0100 @@ -34,7 +34,7 @@ * @author Artem Tikhomirov * @author TMate Software Ltd. */ -public class HgDirstate { +class HgDirstate { private final DataAccessProvider accessProvider; private final File dirstateFile; @@ -143,7 +143,7 @@ - public void dump() { + /*package-local*/ void dump() { read(); @SuppressWarnings("unchecked") Map[] all = new Map[] { normal, added, removed, merged }; diff -r bf304cb14247 -r d55d4eedfc57 src/org/tmatesoft/hg/repo/HgStatusInspector.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/repo/HgStatusInspector.java Thu Jan 27 21:15:21 2011 +0100 @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2010-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.repo; + +import org.tmatesoft.hg.core.Path; + +/** + * Callback to get file status information + * + * @author Artem Tikhomirov + * @author TMate Software Ltd. + */ +public interface HgStatusInspector { + void modified(Path fname); + void added(Path fname); + // XXX need to specify whether StatusCollector invokes added() along with copied or not! + void copied(Path fnameOrigin, Path fnameAdded); // if copied files of no interest, should delegate to self.added(fnameAdded); + void removed(Path fname); + void clean(Path fname); + void missing(Path fname); // aka deleted (tracked by Hg, but not available in FS any more + void unknown(Path fname); // not tracked + void ignored(Path fname); +} \ No newline at end of file diff -r bf304cb14247 -r d55d4eedfc57 src/org/tmatesoft/hg/repo/StatusCollector.java --- a/src/org/tmatesoft/hg/repo/StatusCollector.java Thu Jan 27 06:31:58 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/StatusCollector.java Thu Jan 27 21:15:21 2011 +0100 @@ -21,7 +21,6 @@ import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -31,6 +30,8 @@ import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.core.Path; +import org.tmatesoft.hg.util.PathPool; +import org.tmatesoft.hg.util.PathRewrite; /** @@ -43,10 +44,11 @@ private final HgRepository repo; private final Map cache; // sparse array, in fact + private PathPool pathPool; public StatusCollector(HgRepository hgRepo) { this.repo = hgRepo; - cache = new HashMap(); + cache = new TreeMap(); ManifestRevisionInspector emptyFakeState = new ManifestRevisionInspector(); emptyFakeState.begin(-1, null); emptyFakeState.end(-1); // FIXME HgRepo.TIP == -1 as well, need to distinguish fake "prior to first" revision from "the very last" @@ -70,9 +72,20 @@ /*package-local*/ ManifestRevisionInspector raw(int rev) { return get(rev); } + /*package-local*/ PathPool getPathPool() { + if (pathPool == null) { + pathPool = new PathPool(new PathRewrite.Empty()); + } + return pathPool; + } + + public void setPathPool(PathPool pathPool) { + this.pathPool = pathPool; + } + // hg status --change - public void change(int rev, Inspector inspector) { + public void change(int rev, HgStatusInspector inspector) { int[] parents = new int[2]; repo.getChangelog().parents(rev, parents, null, null); walk(parents[0], rev, inspector); @@ -81,7 +94,7 @@ // I assume revision numbers are the same for changelog and manifest - here // user would like to pass changelog revision numbers, and I use them directly to walk manifest. // if this assumption is wrong, fix this (lookup manifest revisions from changeset). - public void walk(int rev1, int rev2, Inspector inspector) { + public void walk(int rev1, int rev2, HgStatusInspector inspector) { if (rev1 == rev2) { throw new IllegalArgumentException(); } @@ -133,7 +146,8 @@ r1 = get(rev1); r2 = get(rev2); - + PathPool pp = getPathPool(); + TreeSet r1Files = new TreeSet(r1.files()); for (String fname : r2.files()) { if (r1Files.remove(fname)) { @@ -142,23 +156,22 @@ String flagsR1 = r1.flags(fname); String flagsR2 = r2.flags(fname); if (nidR1.equals(nidR2) && ((flagsR2 == null && flagsR1 == null) || flagsR2.equals(flagsR1))) { - inspector.clean(fname); + inspector.clean(pp.path(fname)); } else { - inspector.modified(fname); + inspector.modified(pp.path(fname)); } } else { String copyOrigin = getOriginIfCopy(repo, fname, r1Files, rev1); if (copyOrigin != null) { - inspector.copied(copyOrigin, fname); + inspector.copied(pp.path(copyOrigin), pp.path(fname)); } else { - inspector.added(fname); + inspector.added(pp.path(fname)); } } } for (String left : r1Files) { - inspector.removed(left); + inspector.removed(pp.path(left)); } - // inspector.done() if useful e.g. in UI client } public Record status(int rev1, int rev2) { @@ -188,23 +201,11 @@ return null; } - public interface Inspector { - void modified(String fname); - void added(String fname); - // XXX need to specify whether StatusCollector invokes added() along with copied or not! - void copied(String fnameOrigin, String fnameAdded); // if copied files of no interest, should delegate to self.added(fnameAdded); - void removed(String fname); - void clean(String fname); - void missing(String fname); // aka deleted (tracked by Hg, but not available in FS any more - void unknown(String fname); // not tracked - void ignored(String fname); - } - // XXX for r1..r2 status, only modified, added, removed (and perhaps, clean) make sense // XXX Need to specify whether copy targets are in added or not (@see Inspector#copied above) - public static class Record implements Inspector { - private List modified, added, removed, clean, missing, unknown, ignored; - private Map copied; + public static class Record implements HgStatusInspector { + private List modified, added, removed, clean, missing, unknown, ignored; + private Map copied; private int startRev, endRev; private StatusCollector statusHelper; @@ -222,61 +223,61 @@ statusHelper = self; } - public Nodeid nodeidBeforeChange(String fname) { + public Nodeid nodeidBeforeChange(Path fname) { if (statusHelper == null || startRev == BAD_REVISION) { return null; } if ((modified == null || !modified.contains(fname)) && (removed == null || !removed.contains(fname))) { return null; } - return statusHelper.raw(startRev).nodeid(fname); + return statusHelper.raw(startRev).nodeid(fname.toString()); } - public Nodeid nodeidAfterChange(String fname) { + public Nodeid nodeidAfterChange(Path fname) { if (statusHelper == null || endRev == BAD_REVISION) { return null; } if ((modified == null || !modified.contains(fname)) && (added == null || !added.contains(fname))) { return null; } - return statusHelper.raw(endRev).nodeid(fname); + return statusHelper.raw(endRev).nodeid(fname.toString()); } - public List getModified() { + public List getModified() { return proper(modified); } - public List getAdded() { + public List getAdded() { return proper(added); } - public List getRemoved() { + public List getRemoved() { return proper(removed); } - public Map getCopied() { + public Map getCopied() { if (copied == null) { return Collections.emptyMap(); } return Collections.unmodifiableMap(copied); } - public List getClean() { + public List getClean() { return proper(clean); } - public List getMissing() { + public List getMissing() { return proper(missing); } - public List getUnknown() { + public List getUnknown() { return proper(unknown); } - public List getIgnored() { + public List getIgnored() { return proper(ignored); } - private List proper(List l) { + private List proper(List l) { if (l == null) { return Collections.emptyList(); } @@ -286,47 +287,47 @@ // // - public void modified(String fname) { + public void modified(Path fname) { modified = doAdd(modified, fname); } - public void added(String fname) { + public void added(Path fname) { added = doAdd(added, fname); } - public void copied(String fnameOrigin, String fnameAdded) { + public void copied(Path fnameOrigin, Path fnameAdded) { if (copied == null) { - copied = new LinkedHashMap(); + copied = new LinkedHashMap(); } added(fnameAdded); copied.put(fnameAdded, fnameOrigin); } - public void removed(String fname) { + public void removed(Path fname) { removed = doAdd(removed, fname); } - public void clean(String fname) { + public void clean(Path fname) { clean = doAdd(clean, fname); } - public void missing(String fname) { + public void missing(Path fname) { missing = doAdd(missing, fname); } - public void unknown(String fname) { + public void unknown(Path fname) { unknown = doAdd(unknown, fname); } - public void ignored(String fname) { + public void ignored(Path fname) { ignored = doAdd(ignored, fname); } - private static List doAdd(List l, String s) { + private static List doAdd(List l, Path p) { if (l == null) { - l = new LinkedList(); + l = new LinkedList(); } - l.add(s); + l.add(p); return l; } } diff -r bf304cb14247 -r d55d4eedfc57 src/org/tmatesoft/hg/repo/WorkingCopyStatusCollector.java --- a/src/org/tmatesoft/hg/repo/WorkingCopyStatusCollector.java Thu Jan 27 06:31:58 2011 +0100 +++ b/src/org/tmatesoft/hg/repo/WorkingCopyStatusCollector.java Thu Jan 27 21:15:21 2011 +0100 @@ -30,6 +30,8 @@ import org.tmatesoft.hg.core.Nodeid; import org.tmatesoft.hg.repo.StatusCollector.ManifestRevisionInspector; import org.tmatesoft.hg.util.FileWalker; +import org.tmatesoft.hg.util.PathPool; +import org.tmatesoft.hg.util.PathRewrite; /** * @@ -42,6 +44,7 @@ private final FileWalker repoWalker; private HgDirstate dirstate; private StatusCollector baseRevisionCollector; + private PathPool pathPool; public WorkingCopyStatusCollector(HgRepository hgRepo) { this(hgRepo, hgRepo.createWorkingDirWalker()); @@ -59,6 +62,22 @@ public void setBaseRevisionCollector(StatusCollector sc) { baseRevisionCollector = sc; } + + /*package-local*/ PathPool getPathPool() { + if (pathPool == null) { + if (baseRevisionCollector == null) { + pathPool = new PathPool(new PathRewrite.Empty()); + } else { + return baseRevisionCollector.getPathPool(); + } + } + return pathPool; + } + + public void setPathPool(PathPool pathPool) { + this.pathPool = pathPool; + } + private HgDirstate getDirstate() { if (dirstate == null) { @@ -68,7 +87,7 @@ } // may be invoked few times - public void walk(int baseRevision, StatusCollector.Inspector inspector) { + public void walk(int baseRevision, HgStatusInspector inspector) { final HgIgnore hgIgnore = repo.getIgnore(); TreeSet knownEntries = getDirstate().all(); final boolean isTipBase; @@ -94,12 +113,13 @@ ((StatusCollector.Record) inspector).init(baseRevision, BAD_REVISION, sc); } repoWalker.reset(); + final PathPool pp = getPathPool(); while (repoWalker.hasNext()) { repoWalker.next(); String fname = repoWalker.name(); File f = repoWalker.file(); if (hgIgnore.isIgnored(fname)) { - inspector.ignored(fname); + inspector.ignored(pp.path(fname)); } else if (knownEntries.remove(fname)) { // modified, added, removed, clean if (collect != null) { // need to check against base revision, not FS file @@ -109,24 +129,24 @@ checkLocalStatusAgainstFile(fname, f, inspector); } } else { - inspector.unknown(fname); + inspector.unknown(pp.path(fname)); } } if (collect != null) { for (String r : baseRevFiles) { - inspector.removed(r); + inspector.removed(pp.path(r)); } } for (String m : knownEntries) { // missing known file from a working dir if (getDirstate().checkRemoved(m) == null) { // not removed from the repository = 'deleted' - inspector.missing(m); + inspector.missing(pp.path(m)); } else { // removed from the repo // if we check against non-tip revision, do not report files that were added past that revision and now removed. if (collect == null || baseRevFiles.contains(m)) { - inspector.removed(m); + inspector.removed(pp.path(m)); } } } @@ -141,31 +161,31 @@ //******************************************** - private void checkLocalStatusAgainstFile(String fname, File f, StatusCollector.Inspector inspector) { + private void checkLocalStatusAgainstFile(String fname, File f, HgStatusInspector inspector) { HgDirstate.Record r; if ((r = getDirstate().checkNormal(fname)) != null) { // either clean or modified if (f.lastModified() / 1000 == r.time && r.size == f.length()) { - inspector.clean(fname); + inspector.clean(getPathPool().path(fname)); } else { // FIXME check actual content to avoid false modified files - inspector.modified(fname); + inspector.modified(getPathPool().path(fname)); } } else if ((r = getDirstate().checkAdded(fname)) != null) { if (r.name2 == null) { - inspector.added(fname); + inspector.added(getPathPool().path(fname)); } else { - inspector.copied(r.name2, fname); + inspector.copied(getPathPool().path(r.name2), getPathPool().path(fname)); } } else if ((r = getDirstate().checkRemoved(fname)) != null) { - inspector.removed(fname); + inspector.removed(getPathPool().path(fname)); } else if ((r = getDirstate().checkMerged(fname)) != null) { - inspector.modified(fname); + inspector.modified(getPathPool().path(fname)); } } // XXX refactor checkLocalStatus methods in more OO way - private void checkLocalStatusAgainstBaseRevision(Set baseRevNames, ManifestRevisionInspector collect, int baseRevision, String fname, File f, StatusCollector.Inspector inspector) { + private void checkLocalStatusAgainstBaseRevision(Set baseRevNames, ManifestRevisionInspector collect, int baseRevision, String fname, File f, HgStatusInspector inspector) { // fname is in the dirstate, either Normal, Added, Removed or Merged Nodeid nid1 = collect.nodeid(fname); String flags = collect.flags(fname); @@ -177,13 +197,13 @@ if ((r = getDirstate().checkNormal(fname)) != null) { String origin = StatusCollector.getOriginIfCopy(repo, fname, baseRevNames, baseRevision); if (origin != null) { - inspector.copied(origin, fname); + inspector.copied(getPathPool().path(origin), getPathPool().path(fname)); return; } } else if ((r = getDirstate().checkAdded(fname)) != null) { if (r.name2 != null && baseRevNames.contains(r.name2)) { baseRevNames.remove(r.name2); // XXX surely I shall not report rename source as Removed? - inspector.copied(r.name2, fname); + inspector.copied(getPathPool().path(r.name2), getPathPool().path(fname)); return; } // fall-through, report as added @@ -191,7 +211,7 @@ // removed: removed file was not known at the time of baseRevision, and we should not report it as removed return; } - inspector.added(fname); + inspector.added(getPathPool().path(fname)); } else { // was known; check whether clean or modified // when added - seems to be the case of a file added once again, hence need to check if content is different @@ -200,14 +220,14 @@ HgDataFile fileNode = repo.getFileNode(fname); final int lengthAtRevision = fileNode.length(nid1); if (r.size /* XXX File.length() ?! */ != lengthAtRevision || flags != todoGenerateFlags(fname /*java.io.File*/)) { - inspector.modified(fname); + inspector.modified(getPathPool().path(fname)); } else { // check actual content to see actual changes // XXX consider adding HgDataDile.compare(File/byte[]/whatever) operation to optimize comparison if (areTheSame(f, fileNode.content(nid1))) { - inspector.clean(fname); + inspector.clean(getPathPool().path(fname)); } else { - inspector.modified(fname); + inspector.modified(getPathPool().path(fname)); } } } diff -r bf304cb14247 -r d55d4eedfc57 src/org/tmatesoft/hg/util/PathRewrite.java --- a/src/org/tmatesoft/hg/util/PathRewrite.java Thu Jan 27 06:31:58 2011 +0100 +++ b/src/org/tmatesoft/hg/util/PathRewrite.java Thu Jan 27 21:15:21 2011 +0100 @@ -27,6 +27,12 @@ public interface PathRewrite { public String rewrite(String path); + + public static class Empty implements PathRewrite { + public String rewrite(String path) { + return path; + } + } public class Composite implements PathRewrite { private List chain; diff -r bf304cb14247 -r d55d4eedfc57 test/org/tmatesoft/hg/test/StatusOutputParser.java --- a/test/org/tmatesoft/hg/test/StatusOutputParser.java Thu Jan 27 06:31:58 2011 +0100 +++ b/test/org/tmatesoft/hg/test/StatusOutputParser.java Thu Jan 27 21:15:21 2011 +0100 @@ -17,14 +17,17 @@ package org.tmatesoft.hg.test; import java.io.File; -import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.tmatesoft.hg.core.Path; +import org.tmatesoft.hg.repo.StatusCollector; +import org.tmatesoft.hg.util.PathPool; +import org.tmatesoft.hg.util.PathRewrite; + /** * * @author Artem Tikhomirov @@ -33,61 +36,70 @@ public class StatusOutputParser implements OutputParser { private final Pattern pattern; - private List modified, added, removed, clean, missing, unknown, ignored; - private Map copied; - private final boolean winPathSeparator; + // although using StatusCollector.Record is not really quite honest for testing, + // it's deemed acceptable as long as that class is primitive 'collect all results' + private StatusCollector.Record result = new StatusCollector.Record(); + private final PathPool pathHelper; public StatusOutputParser() { // pattern = Pattern.compile("^([MAR?IC! ]) ([\\w \\.-/\\\\]+)$", Pattern.MULTILINE); pattern = Pattern.compile("^([MAR?IC! ]) (.+)$", Pattern.MULTILINE); - winPathSeparator = File.separatorChar == '\\'; + pathHelper = new PathPool(new PathRewrite() { + + private final boolean winPathSeparator = File.separatorChar == '\\'; + + public String rewrite(String s) { + if (winPathSeparator) { + // Java impl always give slashed path, while Hg uses local, os-specific convention + s = s.replace('\\', '/'); + } + return s; + } + }); } public void reset() { - modified = added = removed = clean = missing = unknown = ignored = null; - copied = null; + result = new StatusCollector.Record(); } public void parse(CharSequence seq) { Matcher m = pattern.matcher(seq); + Path lastAdded = null; while (m.find()) { String fname = m.group(2); switch ((int) m.group(1).charAt(0)) { case (int) 'M' : { - modified = doAdd(modified, fname); + result.modified(pathHelper.path(fname)); break; } case (int) 'A' : { - added = doAdd(added, fname); + result.added(lastAdded = pathHelper.path(fname)); break; } case (int) 'R' : { - removed = doAdd(removed, fname); + result.removed(pathHelper.path(fname)); break; } case (int) '?' : { - unknown = doAdd(unknown, fname); + result.unknown(pathHelper.path(fname)); break; } case (int) 'I' : { - ignored = doAdd(ignored, fname); + result.ignored(pathHelper.path(fname)); break; } case (int) 'C' : { - clean = doAdd(clean, fname); + result.clean(pathHelper.path(fname)); break; } case (int) '!' : { - missing = doAdd(missing, fname); + result.missing(pathHelper.path(fname)); break; } case (int) ' ' : { - if (copied == null) { - copied = new TreeMap(); - } // last added is copy destination // to get or to remove it - depends on what StatusCollector does in this case - copied.put(added.get(added.size() - 1), toJavaImplConvention(fname)); + result.copied(pathHelper.path(fname), lastAdded); break; } } @@ -95,61 +107,39 @@ } // - public List getModified() { - return proper(modified); - } - - public List getAdded() { - return proper(added); - } - - public List getRemoved() { - return proper(removed); - } - - public Map getCopied() { - if (copied == null) { - return Collections.emptyMap(); - } - return Collections.unmodifiableMap(copied); - } - - public List getClean() { - return proper(clean); - } - - public List getMissing() { - return proper(missing); + public List getModified() { + return result.getModified(); } - public List getUnknown() { - return proper(unknown); - } - - public List getIgnored() { - return proper(ignored); - } - - private List proper(List l) { - if (l == null) { - return Collections.emptyList(); + public List getAdded() { + List rv = new LinkedList(result.getAdded()); + for (Path p : result.getCopied().keySet()) { + rv.remove(p); // remove only one duplicate } - return Collections.unmodifiableList(l); + return rv; } - private List doAdd(List l, String s) { - if (l == null) { - l = new LinkedList(); - } - l.add(toJavaImplConvention(s)); - return l; + public List getRemoved() { + return result.getRemoved(); } - private String toJavaImplConvention(String s) { - if (winPathSeparator) { - // Java impl always give slashed path, while Hg uses local, os-specific convention - s = s.replace('\\', '/'); - } - return s; + public Map getCopied() { + return result.getCopied(); + } + + public List getClean() { + return result.getClean(); + } + + public List getMissing() { + return result.getMissing(); + } + + public List getUnknown() { + return result.getUnknown(); + } + + public List getIgnored() { + return result.getIgnored(); } } diff -r bf304cb14247 -r d55d4eedfc57 test/org/tmatesoft/hg/test/TestStatus.java --- a/test/org/tmatesoft/hg/test/TestStatus.java Thu Jan 27 06:31:58 2011 +0100 +++ b/test/org/tmatesoft/hg/test/TestStatus.java Thu Jan 27 21:15:21 2011 +0100 @@ -22,8 +22,8 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; -import java.util.Map; +import org.tmatesoft.hg.core.Path; import org.tmatesoft.hg.core.StatusCommand; import org.tmatesoft.hg.repo.HgRepository; import org.tmatesoft.hg.repo.Lookup; @@ -121,13 +121,13 @@ reportNotEqual("IGNORED", r.getIgnored(), statusParser.getIgnored()); reportNotEqual("MISSING", r.getMissing(), statusParser.getMissing()); reportNotEqual("UNKNOWN", r.getUnknown(), statusParser.getUnknown()); - List copiedKeyDiff = difference(r.getCopied().keySet(), statusParser.getCopied().keySet()); - HashMap copyDiff = new HashMap(); + List copiedKeyDiff = difference(r.getCopied().keySet(), statusParser.getCopied().keySet()); + HashMap copyDiff = new HashMap(); if (copiedKeyDiff.isEmpty()) { - for (String jk : r.getCopied().keySet()) { - String jv = r.getCopied().get(jk); + for (Path jk : r.getCopied().keySet()) { + Path jv = r.getCopied().get(jk); if (statusParser.getCopied().containsKey(jk)) { - String cmdv = statusParser.getCopied().get(jk); + Path cmdv = statusParser.getCopied().get(jk); if (!jv.equals(cmdv)) { copyDiff.put(jk, jv + " instead of " + cmdv); } @@ -137,10 +137,10 @@ } } System.out.println("COPIED" + (copiedKeyDiff.isEmpty() && copyDiff.isEmpty() ? " are the same" : " are NOT the same:")); - for (String s : copiedKeyDiff) { + for (Path s : copiedKeyDiff) { System.out.println("\tNon-matching key:" + s); } - for (String s : copyDiff.keySet()) { + for (Path s : copyDiff.keySet()) { System.out.println(s + " : " + copyDiff.get(s)); } // TODO compare equals