Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/StatusCollector.java @ 89:42bcb4bffd17
Refactored to simplify manifest collector
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Wed, 26 Jan 2011 06:18:31 +0100 |
| parents | 61eedab3eb3e |
| children | a95c700408a9 |
comparison
equal
deleted
inserted
replaced
| 88:61eedab3eb3e | 89:42bcb4bffd17 |
|---|---|
| 24 import java.util.HashMap; | 24 import java.util.HashMap; |
| 25 import java.util.LinkedHashMap; | 25 import java.util.LinkedHashMap; |
| 26 import java.util.LinkedList; | 26 import java.util.LinkedList; |
| 27 import java.util.List; | 27 import java.util.List; |
| 28 import java.util.Map; | 28 import java.util.Map; |
| 29 import java.util.TreeMap; | |
| 29 import java.util.TreeSet; | 30 import java.util.TreeSet; |
| 30 | 31 |
| 31 import org.tmatesoft.hg.core.Nodeid; | 32 import org.tmatesoft.hg.core.Nodeid; |
| 32 import org.tmatesoft.hg.core.Path; | 33 import org.tmatesoft.hg.core.Path; |
| 33 | 34 |
| 44 private final Map<Integer, ManifestRevisionInspector> cache; // sparse array, in fact | 45 private final Map<Integer, ManifestRevisionInspector> cache; // sparse array, in fact |
| 45 | 46 |
| 46 public StatusCollector(HgRepository hgRepo) { | 47 public StatusCollector(HgRepository hgRepo) { |
| 47 this.repo = hgRepo; | 48 this.repo = hgRepo; |
| 48 cache = new HashMap<Integer, ManifestRevisionInspector>(); | 49 cache = new HashMap<Integer, ManifestRevisionInspector>(); |
| 49 ManifestRevisionInspector emptyFakeState = new ManifestRevisionInspector(-1, -1); | 50 ManifestRevisionInspector emptyFakeState = new ManifestRevisionInspector(); |
| 50 emptyFakeState.begin(-1, null); | 51 emptyFakeState.begin(-1, null); |
| 51 emptyFakeState.end(-1); // FIXME HgRepo.TIP == -1 as well, need to distinguish fake "prior to first" revision from "the very last" | 52 emptyFakeState.end(-1); // FIXME HgRepo.TIP == -1 as well, need to distinguish fake "prior to first" revision from "the very last" |
| 52 cache.put(-1, emptyFakeState); | 53 cache.put(-1, emptyFakeState); |
| 53 } | 54 } |
| 54 | 55 |
| 57 } | 58 } |
| 58 | 59 |
| 59 private ManifestRevisionInspector get(int rev) { | 60 private ManifestRevisionInspector get(int rev) { |
| 60 ManifestRevisionInspector i = cache.get(rev); | 61 ManifestRevisionInspector i = cache.get(rev); |
| 61 if (i == null) { | 62 if (i == null) { |
| 62 i = new ManifestRevisionInspector(rev, rev); | 63 i = new ManifestRevisionInspector(); |
| 63 cache.put(rev, i); | 64 cache.put(rev, i); |
| 64 repo.getManifest().walk(rev, rev, i); | 65 repo.getManifest().walk(rev, rev, i); |
| 65 } | 66 } |
| 66 return i; | 67 return i; |
| 67 } | 68 } |
| 96 if (rev2 == TIP) { | 97 if (rev2 == TIP) { |
| 97 rev2 = repo.getManifest().getRevisionCount() - 1; // XXX add Revlog.tip() func ? | 98 rev2 = repo.getManifest().getRevisionCount() - 1; // XXX add Revlog.tip() func ? |
| 98 } | 99 } |
| 99 // in fact, rev1 and rev2 are often next (or close) to each other, | 100 // in fact, rev1 and rev2 are often next (or close) to each other, |
| 100 // thus, we can optimize Manifest reads here (manifest.walk(rev1, rev2)) | 101 // thus, we can optimize Manifest reads here (manifest.walk(rev1, rev2)) |
| 101 ManifestRevisionInspector r1, r2; | 102 ManifestRevisionInspector r1, r2 ; |
| 102 if (!cache.containsKey(rev1) && !cache.containsKey(rev2) && Math.abs(rev1 - rev2) < 5 /*subjective equivalent of 'close enough'*/) { | 103 if (!cache.containsKey(rev1) && !cache.containsKey(rev2) && Math.abs(rev1 - rev2) < 5 /*subjective equivalent of 'close enough'*/) { |
| 103 int minRev = rev1 < rev2 ? rev1 : rev2; | 104 int minRev = rev1 < rev2 ? rev1 : rev2; |
| 104 int maxRev = minRev == rev1 ? rev2 : rev1; | 105 int maxRev = minRev == rev1 ? rev2 : rev1; |
| 105 r1 = r2 = new ManifestRevisionInspector(minRev, maxRev); | 106 if (minRev > 0) { |
| 106 for (int i = minRev; i <= maxRev; i++) { | 107 minRev--; // expand range a bit |
| 107 cache.put(i, r1); | 108 // XXX perhaps, if revlog.baseRevision is cheap, shall expand minRev up to baseRevision |
| 108 } | 109 // which gonna be read anyway |
| 109 repo.getManifest().walk(minRev, maxRev, r1); | 110 } |
| 110 } else { | 111 |
| 111 r1 = get(rev1); | 112 repo.getManifest().walk(minRev, maxRev, new HgManifest.Inspector() { |
| 112 r2 = get(rev2); | 113 private ManifestRevisionInspector delegate; |
| 113 } | 114 |
| 114 | 115 public boolean begin(int revision, Nodeid nid) { |
| 115 TreeSet<String> r1Files = new TreeSet<String>(r1.files(rev1)); | 116 cache.put(revision, delegate = new ManifestRevisionInspector()); |
| 116 for (String fname : r2.files(rev2)) { | 117 delegate.begin(revision, nid); |
| 118 return true; | |
| 119 } | |
| 120 | |
| 121 public boolean next(Nodeid nid, String fname, String flags) { | |
| 122 delegate.next(nid, fname, flags); | |
| 123 return true; | |
| 124 } | |
| 125 | |
| 126 public boolean end(int revision) { | |
| 127 delegate.end(revision); | |
| 128 delegate = null; | |
| 129 return true; | |
| 130 } | |
| 131 }); | |
| 132 } | |
| 133 r1 = get(rev1); | |
| 134 r2 = get(rev2); | |
| 135 | |
| 136 | |
| 137 TreeSet<String> r1Files = new TreeSet<String>(r1.files()); | |
| 138 for (String fname : r2.files()) { | |
| 117 if (r1Files.remove(fname)) { | 139 if (r1Files.remove(fname)) { |
| 118 Nodeid nidR1 = r1.nodeid(rev1, fname); | 140 Nodeid nidR1 = r1.nodeid(fname); |
| 119 Nodeid nidR2 = r2.nodeid(rev2, fname); | 141 Nodeid nidR2 = r2.nodeid(fname); |
| 120 String flagsR1 = r1.flags(rev1, fname); | 142 String flagsR1 = r1.flags(fname); |
| 121 String flagsR2 = r2.flags(rev2, fname); | 143 String flagsR2 = r2.flags(fname); |
| 122 if (nidR1.equals(nidR2) && ((flagsR2 == null && flagsR1 == null) || flagsR2.equals(flagsR1))) { | 144 if (nidR1.equals(nidR2) && ((flagsR2 == null && flagsR1 == null) || flagsR2.equals(flagsR1))) { |
| 123 inspector.clean(fname); | 145 inspector.clean(fname); |
| 124 } else { | 146 } else { |
| 125 inspector.modified(fname); | 147 inspector.modified(fname); |
| 126 } | 148 } |
| 200 return null; | 222 return null; |
| 201 } | 223 } |
| 202 if ((modified == null || !modified.contains(fname)) && (removed == null || !removed.contains(fname))) { | 224 if ((modified == null || !modified.contains(fname)) && (removed == null || !removed.contains(fname))) { |
| 203 return null; | 225 return null; |
| 204 } | 226 } |
| 205 return statusHelper.raw(startRev).nodeid(startRev, fname); | 227 return statusHelper.raw(startRev).nodeid(fname); |
| 206 } | 228 } |
| 207 public Nodeid nodeidAfterChange(String fname) { | 229 public Nodeid nodeidAfterChange(String fname) { |
| 208 if (statusHelper == null || endRev == BAD_REVISION) { | 230 if (statusHelper == null || endRev == BAD_REVISION) { |
| 209 return null; | 231 return null; |
| 210 } | 232 } |
| 211 if ((modified == null || !modified.contains(fname)) && (added == null || !added.contains(fname))) { | 233 if ((modified == null || !modified.contains(fname)) && (added == null || !added.contains(fname))) { |
| 212 return null; | 234 return null; |
| 213 } | 235 } |
| 214 return statusHelper.raw(endRev).nodeid(endRev, fname); | 236 return statusHelper.raw(endRev).nodeid(fname); |
| 215 } | 237 } |
| 216 | 238 |
| 217 public List<String> getModified() { | 239 public List<String> getModified() { |
| 218 return proper(modified); | 240 return proper(modified); |
| 219 } | 241 } |
| 302 l.add(s); | 324 l.add(s); |
| 303 return l; | 325 return l; |
| 304 } | 326 } |
| 305 } | 327 } |
| 306 | 328 |
| 307 // XXX in fact, indexed access brings more trouble than benefits, get rid of it? Distinct instance per revision is good enough | 329 /*package-local*/ static final class ManifestRevisionInspector implements HgManifest.Inspector { |
| 308 public /*XXX private, actually. Made public unless repo.statusLocal finds better place*/ static final class ManifestRevisionInspector implements HgManifest.Inspector { | 330 private final TreeMap<String, Nodeid> idsMap; |
| 309 private final HashMap<String, Nodeid>[] idsMap; | 331 private final TreeMap<String, String> flagsMap; |
| 310 private final HashMap<String, String>[] flagsMap; | 332 |
| 311 private final int baseRevision; | 333 public ManifestRevisionInspector() { |
| 312 private int r = -1; // cursor | 334 idsMap = new TreeMap<String, Nodeid>(); |
| 313 | 335 flagsMap = new TreeMap<String, String>(); |
| 314 /** | 336 } |
| 315 * [minRev, maxRev] | 337 |
| 316 * [-1,-1] also accepted (for fake empty instance) | 338 public Collection<String> files() { |
| 317 * @param minRev - inclusive | 339 return idsMap.keySet(); |
| 318 * @param maxRev - inclusive | 340 } |
| 319 */ | 341 |
| 320 @SuppressWarnings("unchecked") | 342 public Nodeid nodeid(String fname) { |
| 321 public ManifestRevisionInspector(int minRev, int maxRev) { | 343 return idsMap.get(fname); |
| 322 baseRevision = minRev; | 344 } |
| 323 int range = maxRev - minRev + 1; | 345 |
| 324 idsMap = new HashMap[range]; | 346 public String flags(String fname) { |
| 325 flagsMap = new HashMap[range]; | 347 return flagsMap.get(fname); |
| 326 } | |
| 327 | |
| 328 public Collection<String> files(int rev) { | |
| 329 if (rev < baseRevision || rev >= baseRevision + idsMap.length) { | |
| 330 throw new IllegalArgumentException(); | |
| 331 } | |
| 332 return idsMap[rev - baseRevision].keySet(); | |
| 333 } | |
| 334 | |
| 335 public Nodeid nodeid(int rev, String fname) { | |
| 336 if (rev < baseRevision || rev >= baseRevision + idsMap.length) { | |
| 337 throw new IllegalArgumentException(); | |
| 338 } | |
| 339 return idsMap[rev - baseRevision].get(fname); | |
| 340 } | |
| 341 | |
| 342 public String flags(int rev, String fname) { | |
| 343 if (rev < baseRevision || rev >= baseRevision + idsMap.length) { | |
| 344 throw new IllegalArgumentException(); | |
| 345 } | |
| 346 return flagsMap[rev - baseRevision].get(fname); | |
| 347 } | 348 } |
| 348 | 349 |
| 349 // | 350 // |
| 350 | 351 |
| 351 public boolean next(Nodeid nid, String fname, String flags) { | 352 public boolean next(Nodeid nid, String fname, String flags) { |
| 352 idsMap[r].put(fname, nid); | 353 idsMap.put(fname, nid); |
| 353 flagsMap[r].put(fname, flags); | 354 flagsMap.put(fname, flags); |
| 354 return true; | 355 return true; |
| 355 } | 356 } |
| 356 | 357 |
| 357 public boolean end(int revision) { | 358 public boolean end(int revision) { |
| 358 assert revision == r + baseRevision; | 359 // in fact, this class cares about single revision |
| 359 r = -1; | 360 return false; |
| 360 return revision+1 < baseRevision + idsMap.length; | |
| 361 } | 361 } |
| 362 | 362 |
| 363 public boolean begin(int revision, Nodeid nid) { | 363 public boolean begin(int revision, Nodeid nid) { |
| 364 if (revision < baseRevision || revision >= baseRevision + idsMap.length) { | |
| 365 throw new IllegalArgumentException(); | |
| 366 } | |
| 367 r = revision - baseRevision; | |
| 368 idsMap[r] = new HashMap<String, Nodeid>(); | |
| 369 flagsMap[r] = new HashMap<String, String>(); | |
| 370 return true; | 364 return true; |
| 371 } | 365 } |
| 372 } | 366 } |
| 373 | 367 |
| 374 } | 368 } |
