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 }