Mercurial > jhg
comparison src/com/tmate/hgkit/ll/StatusCollector.java @ 55:05829a70b30b
Status operation extracted into separate, cache-friendly class
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Mon, 17 Jan 2011 04:45:09 +0100 |
| parents | |
| children | 576d6e8a09f6 |
comparison
equal
deleted
inserted
replaced
| 54:fd4f2c98995b | 55:05829a70b30b |
|---|---|
| 1 /* | |
| 2 * Copyright (c) 2011 Artem Tikhomirov | |
| 3 */ | |
| 4 package com.tmate.hgkit.ll; | |
| 5 | |
| 6 import java.util.Collection; | |
| 7 import java.util.Collections; | |
| 8 import java.util.HashMap; | |
| 9 import java.util.LinkedHashMap; | |
| 10 import java.util.LinkedList; | |
| 11 import java.util.List; | |
| 12 import java.util.Map; | |
| 13 import java.util.TreeSet; | |
| 14 | |
| 15 /** | |
| 16 * RevisionWalker? | |
| 17 * @author artem | |
| 18 */ | |
| 19 public class StatusCollector { | |
| 20 | |
| 21 private final HgRepository repo; | |
| 22 private final Map<Integer, ManifestRevisionInspector> cache; // sparse array, in fact | |
| 23 | |
| 24 public StatusCollector(HgRepository hgRepo) { | |
| 25 this.repo = hgRepo; | |
| 26 cache = new HashMap<Integer, ManifestRevisionInspector>(); | |
| 27 } | |
| 28 | |
| 29 private ManifestRevisionInspector get(int rev) { | |
| 30 ManifestRevisionInspector i = cache.get(rev); | |
| 31 if (i == null) { | |
| 32 i = new ManifestRevisionInspector(rev, rev); | |
| 33 cache.put(rev, i); | |
| 34 repo.getManifest().walk(rev, rev, i); | |
| 35 } | |
| 36 return i; | |
| 37 } | |
| 38 | |
| 39 public void walk(int rev1, int rev2, Inspector inspector) { | |
| 40 if (rev1 == rev2) { | |
| 41 throw new IllegalArgumentException(); | |
| 42 } | |
| 43 // in fact, rev1 and rev2 are often next (or close) to each other, | |
| 44 // thus, we can optimize Manifest reads here (manifest.walk(rev1, rev2)) | |
| 45 | |
| 46 ManifestRevisionInspector r1, r2; | |
| 47 if (!cache.containsKey(rev1) && !cache.containsKey(rev2) && Math.abs(rev1 - rev2) < 5 /*subjective equivalent of 'close enough'*/) { | |
| 48 int minRev = rev1 < rev2 ? rev1 : rev2; | |
| 49 int maxRev = minRev == rev1 ? rev2 : rev1; | |
| 50 r1 = r2 = new ManifestRevisionInspector(minRev, maxRev); | |
| 51 for (int i = minRev; i <= maxRev; i++) { | |
| 52 cache.put(i, r1); | |
| 53 } | |
| 54 repo.getManifest().walk(minRev, maxRev, r1); | |
| 55 } else { | |
| 56 r1 = get(rev1); | |
| 57 r2 = get(rev2); | |
| 58 } | |
| 59 | |
| 60 TreeSet<String> r1Files = new TreeSet<String>(r1.files(rev1)); | |
| 61 for (String fname : r2.files(rev2)) { | |
| 62 if (r1Files.remove(fname)) { | |
| 63 Nodeid nidR1 = r1.nodeid(rev1, fname); | |
| 64 Nodeid nidR2 = r2.nodeid(rev2, fname); | |
| 65 String flagsR1 = r1.flags(rev1, fname); | |
| 66 String flagsR2 = r2.flags(rev2, fname); | |
| 67 if (nidR1.equals(nidR2) && ((flagsR2 == null && flagsR1 == null) || flagsR2.equals(flagsR1))) { | |
| 68 inspector.clean(fname); | |
| 69 } else { | |
| 70 inspector.modified(fname); | |
| 71 } | |
| 72 } else { | |
| 73 inspector.added(fname); | |
| 74 } | |
| 75 } | |
| 76 for (String left : r1Files) { | |
| 77 inspector.removed(left); | |
| 78 } | |
| 79 // inspector.done() if useful e.g. in UI client | |
| 80 } | |
| 81 | |
| 82 public Record status(int rev1, int rev2) { | |
| 83 Record rv = new Record(); | |
| 84 walk(rev1, rev2, rv); | |
| 85 return rv; | |
| 86 } | |
| 87 | |
| 88 public interface Inspector { | |
| 89 void modified(String fname); | |
| 90 void added(String fname); | |
| 91 void copied(String fnameOrigin, String fnameAdded); // if copied files of no interest, should delegate to self.added(fnameAdded); | |
| 92 void removed(String fname); | |
| 93 void clean(String fname); | |
| 94 void missing(String fname); // aka deleted (tracked by Hg, but not available in FS any more | |
| 95 void unknown(String fname); // not tracked | |
| 96 void ignored(String fname); | |
| 97 } | |
| 98 | |
| 99 // XXX for r1..r2 status, only modified, added, removed (and perhaps, clean) make sense | |
| 100 public static class Record implements Inspector { | |
| 101 private List<String> modified, added, removed, clean, missing, unknown, ignored; | |
| 102 private Map<String, String> copied; | |
| 103 | |
| 104 public List<String> getModified() { | |
| 105 return proper(modified); | |
| 106 } | |
| 107 | |
| 108 public List<String> getAdded() { | |
| 109 return proper(added); | |
| 110 } | |
| 111 | |
| 112 public List<String> getRemoved() { | |
| 113 return proper(removed); | |
| 114 } | |
| 115 | |
| 116 public Map<String,String> getCopied() { | |
| 117 if (copied == null) { | |
| 118 return Collections.emptyMap(); | |
| 119 } | |
| 120 return Collections.unmodifiableMap(copied); | |
| 121 } | |
| 122 | |
| 123 public List<String> getClean() { | |
| 124 return proper(clean); | |
| 125 } | |
| 126 | |
| 127 public List<String> getMissing() { | |
| 128 return proper(missing); | |
| 129 } | |
| 130 | |
| 131 public List<String> getUnknown() { | |
| 132 return proper(unknown); | |
| 133 } | |
| 134 | |
| 135 public List<String> getIgnored() { | |
| 136 return proper(ignored); | |
| 137 } | |
| 138 | |
| 139 private List<String> proper(List<String> l) { | |
| 140 if (l == null) { | |
| 141 return Collections.emptyList(); | |
| 142 } | |
| 143 return Collections.unmodifiableList(l); | |
| 144 } | |
| 145 | |
| 146 // | |
| 147 // | |
| 148 | |
| 149 public void modified(String fname) { | |
| 150 modified = doAdd(modified, fname); | |
| 151 } | |
| 152 | |
| 153 public void added(String fname) { | |
| 154 added = doAdd(added, fname); | |
| 155 } | |
| 156 | |
| 157 public void copied(String fnameOrigin, String fnameAdded) { | |
| 158 if (copied == null) { | |
| 159 copied = new LinkedHashMap<String, String>(); | |
| 160 } | |
| 161 copied.put(fnameOrigin, fnameAdded); | |
| 162 } | |
| 163 | |
| 164 public void removed(String fname) { | |
| 165 removed = doAdd(removed, fname); | |
| 166 } | |
| 167 | |
| 168 public void clean(String fname) { | |
| 169 clean = doAdd(clean, fname); | |
| 170 } | |
| 171 | |
| 172 public void missing(String fname) { | |
| 173 missing = doAdd(missing, fname); | |
| 174 } | |
| 175 | |
| 176 public void unknown(String fname) { | |
| 177 unknown = doAdd(unknown, fname); | |
| 178 } | |
| 179 | |
| 180 public void ignored(String fname) { | |
| 181 ignored = doAdd(ignored, fname); | |
| 182 } | |
| 183 | |
| 184 private static List<String> doAdd(List<String> l, String s) { | |
| 185 if (l == null) { | |
| 186 l = new LinkedList<String>(); | |
| 187 } | |
| 188 l.add(s); | |
| 189 return l; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 public /*XXX private, actually. Made public unless repo.statusLocal finds better place*/ static final class ManifestRevisionInspector implements HgManifest.Inspector { | |
| 194 private final HashMap<String, Nodeid>[] idsMap; | |
| 195 private final HashMap<String, String>[] flagsMap; | |
| 196 private final int baseRevision; | |
| 197 private int r = -1; // cursor | |
| 198 | |
| 199 /** | |
| 200 * [minRev, maxRev] | |
| 201 * @param minRev - inclusive | |
| 202 * @param maxRev - inclusive | |
| 203 */ | |
| 204 @SuppressWarnings("unchecked") | |
| 205 public ManifestRevisionInspector(int minRev, int maxRev) { | |
| 206 baseRevision = minRev; | |
| 207 int range = maxRev - minRev + 1; | |
| 208 idsMap = new HashMap[range]; | |
| 209 flagsMap = new HashMap[range]; | |
| 210 } | |
| 211 | |
| 212 public Collection<String> files(int rev) { | |
| 213 if (rev < baseRevision || rev >= baseRevision + idsMap.length) { | |
| 214 throw new IllegalArgumentException(); | |
| 215 } | |
| 216 return idsMap[rev - baseRevision].keySet(); | |
| 217 } | |
| 218 | |
| 219 public Nodeid nodeid(int rev, String fname) { | |
| 220 if (rev < baseRevision || rev >= baseRevision + idsMap.length) { | |
| 221 throw new IllegalArgumentException(); | |
| 222 } | |
| 223 return idsMap[rev - baseRevision].get(fname); | |
| 224 } | |
| 225 | |
| 226 public String flags(int rev, String fname) { | |
| 227 if (rev < baseRevision || rev >= baseRevision + idsMap.length) { | |
| 228 throw new IllegalArgumentException(); | |
| 229 } | |
| 230 return flagsMap[rev - baseRevision].get(fname); | |
| 231 } | |
| 232 | |
| 233 // | |
| 234 | |
| 235 public boolean next(Nodeid nid, String fname, String flags) { | |
| 236 idsMap[r].put(fname, nid); | |
| 237 flagsMap[r].put(fname, flags); | |
| 238 return true; | |
| 239 } | |
| 240 | |
| 241 public boolean end(int revision) { | |
| 242 assert revision == r + baseRevision; | |
| 243 r = -1; | |
| 244 return revision+1 < baseRevision + idsMap.length; | |
| 245 } | |
| 246 | |
| 247 public boolean begin(int revision, Nodeid nid) { | |
| 248 if (revision < baseRevision || revision >= baseRevision + idsMap.length) { | |
| 249 throw new IllegalArgumentException(); | |
| 250 } | |
| 251 r = revision - baseRevision; | |
| 252 idsMap[r] = new HashMap<String, Nodeid>(); | |
| 253 flagsMap[r] = new HashMap<String, String>(); | |
| 254 return true; | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 } |
