Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgManifest.java @ 403:2747b0723867
FIXMEs: work on exceptions and javadoc
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> | 
|---|---|
| date | Mon, 05 Mar 2012 14:50:51 +0100 | 
| parents | 6952d9ce97f1 | 
| children | 7f27122011c3 | 
   comparison
  equal
  deleted
  inserted
  replaced
| 402:1fcc7f7b6d65 | 403:2747b0723867 | 
|---|---|
| 28 import java.util.Map; | 28 import java.util.Map; | 
| 29 | 29 | 
| 30 import org.tmatesoft.hg.core.HgBadStateException; | 30 import org.tmatesoft.hg.core.HgBadStateException; | 
| 31 import org.tmatesoft.hg.core.HgException; | 31 import org.tmatesoft.hg.core.HgException; | 
| 32 import org.tmatesoft.hg.core.HgInvalidControlFileException; | 32 import org.tmatesoft.hg.core.HgInvalidControlFileException; | 
| 33 import org.tmatesoft.hg.core.HgInvalidRevisionException; | |
| 33 import org.tmatesoft.hg.core.Nodeid; | 34 import org.tmatesoft.hg.core.Nodeid; | 
| 34 import org.tmatesoft.hg.internal.DataAccess; | 35 import org.tmatesoft.hg.internal.DataAccess; | 
| 35 import org.tmatesoft.hg.internal.DigestHelper; | 36 import org.tmatesoft.hg.internal.DigestHelper; | 
| 36 import org.tmatesoft.hg.internal.EncodingHelper; | 37 import org.tmatesoft.hg.internal.EncodingHelper; | 
| 37 import org.tmatesoft.hg.internal.Experimental; | 38 import org.tmatesoft.hg.internal.Experimental; | 
| 127 * <code>Inspector.begin(); Inspector.end()</code> call pair. | 128 * <code>Inspector.begin(); Inspector.end()</code> call pair. | 
| 128 * | 129 * | 
| 129 * @param start changelog (not manifest!) revision to begin with | 130 * @param start changelog (not manifest!) revision to begin with | 
| 130 * @param end changelog (not manifest!) revision to stop, inclusive. | 131 * @param end changelog (not manifest!) revision to stop, inclusive. | 
| 131 * @param inspector manifest revision visitor, can't be <code>null</code> | 132 * @param inspector manifest revision visitor, can't be <code>null</code> | 
| 133 * @throws HgInvalidRevisionException if start or end specify non-existent revision index | |
| 134 * @throws IllegalArgumentException if start or end is not a revision index | |
| 132 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | 135 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | 
| 133 */ | 136 */ | 
| 134 public void walk(int start, int end, final Inspector inspector) throws /*FIXME HgInvalidRevisionException,*/ HgInvalidControlFileException { | 137 public void walk(int start, int end, final Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { | 
| 135 if (inspector == null) { | 138 if (inspector == null) { | 
| 136 throw new IllegalArgumentException(); | 139 throw new IllegalArgumentException(); | 
| 137 } | 140 } | 
| 138 final int csetFirst = start <= end ? start : end, csetLast = start > end ? start : end; | 141 final int csetFirst = start <= end ? start : end, csetLast = start > end ? start : end; | 
| 139 int manifestFirst, manifestLast, i = 0; | 142 int manifestFirst, manifestLast, i = 0; | 
| 140 do { | 143 do { | 
| 141 manifestFirst = fromChangelog(csetFirst+i); | 144 manifestFirst = fromChangelog(csetFirst+i); | 
| 142 if (manifestFirst == -1) { | 145 if (manifestFirst == BAD_REVISION) { | 
| 143 inspector.begin(BAD_REVISION, NULL, csetFirst+i); | 146 inspector.begin(BAD_REVISION, NULL, csetFirst+i); | 
| 144 inspector.end(BAD_REVISION); | 147 inspector.end(BAD_REVISION); | 
| 145 } | 148 } | 
| 146 i++; | 149 i++; | 
| 147 } while (manifestFirst == -1 && csetFirst+i <= csetLast); | 150 } while (manifestFirst == BAD_REVISION && csetFirst+i <= csetLast); | 
| 148 if (manifestFirst == -1) { | 151 if (manifestFirst == BAD_REVISION) { | 
| 149 getRepo().getContext().getLog().info(getClass(), "None of changesets [%d..%d] have associated manifest revision", csetFirst, csetLast); | 152 getRepo().getContext().getLog().info(getClass(), "None of changesets [%d..%d] have associated manifest revision", csetFirst, csetLast); | 
| 150 // we ran through all revisions in [start..end] and none of them had manifest. | 153 // we ran through all revisions in [start..end] and none of them had manifest. | 
| 151 // we reported that to inspector and proceeding is done now. | 154 // we reported that to inspector and proceeding is done now. | 
| 152 return; | 155 return; | 
| 153 } | 156 } | 
| 154 i = 0; | 157 i = 0; | 
| 155 do { | 158 do { | 
| 156 manifestLast = fromChangelog(csetLast-i); | 159 manifestLast = fromChangelog(csetLast-i); | 
| 157 if (manifestLast == -1) { | 160 if (manifestLast == BAD_REVISION) { | 
| 158 inspector.begin(BAD_REVISION, NULL, csetLast-i); | 161 inspector.begin(BAD_REVISION, NULL, csetLast-i); | 
| 159 inspector.end(BAD_REVISION); | 162 inspector.end(BAD_REVISION); | 
| 160 } | 163 } | 
| 161 i++; | 164 i++; | 
| 162 } while (manifestLast == -1 && csetLast-i >= csetFirst); | 165 } while (manifestLast == BAD_REVISION && csetLast-i >= csetFirst); | 
| 163 if (manifestLast == -1) { | 166 if (manifestLast == BAD_REVISION) { | 
| 164 // hmm, manifestFirst != -1 here, hence there's i from [csetFirst..csetLast] for which manifest entry exists, | 167 // hmm, manifestFirst != -1 here, hence there's i from [csetFirst..csetLast] for which manifest entry exists, | 
| 165 // and thus it's impossible to run into manifestLast == -1. Nevertheless, never hurts to check. | 168 // and thus it's impossible to run into manifestLast == -1. Nevertheless, never hurts to check. | 
| 166 throw new HgBadStateException(String.format("Manifest %d-%d(!) for cset range [%d..%d] ", manifestFirst, manifestLast, csetFirst, csetLast)); | 169 throw new HgBadStateException(String.format("Manifest %d-%d(!) for cset range [%d..%d] ", manifestFirst, manifestLast, csetFirst, csetLast)); | 
| 167 } | 170 } | 
| 168 if (manifestLast < manifestFirst) { | 171 if (manifestLast < manifestFirst) { | 
| 181 * gets invoked doesn't resemble order of changeset revisions supplied, manifest revisions are reported in the order they appear | 184 * gets invoked doesn't resemble order of changeset revisions supplied, manifest revisions are reported in the order they appear | 
| 182 * in manifest revlog (with exception of changesets with missing manifest that may be reported in any order). | 185 * in manifest revlog (with exception of changesets with missing manifest that may be reported in any order). | 
| 183 * | 186 * | 
| 184 * @param inspector manifest revision visitor, can't be <code>null</code> | 187 * @param inspector manifest revision visitor, can't be <code>null</code> | 
| 185 * @param revisionIndexes local indexes of changesets to visit, non-<code>null</code> | 188 * @param revisionIndexes local indexes of changesets to visit, non-<code>null</code> | 
| 189 * @throws HgInvalidRevisionException if argument specifies non-existent revision index | |
| 190 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | |
| 186 */ | 191 */ | 
| 187 public void walk(final Inspector inspector, int... revisionIndexes) throws HgInvalidControlFileException{ | 192 public void walk(final Inspector inspector, int... revisionIndexes) throws HgInvalidRevisionException, HgInvalidControlFileException { | 
| 188 if (inspector == null || revisionIndexes == null) { | 193 if (inspector == null || revisionIndexes == null) { | 
| 189 throw new IllegalArgumentException(); | 194 throw new IllegalArgumentException(); | 
| 190 } | 195 } | 
| 191 int[] manifestRevs = toManifestRevisionIndexes(revisionIndexes, inspector); | 196 int[] manifestRevs = toManifestRevisionIndexes(revisionIndexes, inspector); | 
| 192 content.iterate(manifestRevs, true, new ManifestParser(inspector)); | 197 content.iterate(manifestRevs, true, new ManifestParser(inspector)); | 
| 193 } | 198 } | 
| 194 | 199 | 
| 195 // | 200 // | 
| 196 /** | 201 /** | 
| 197 * Tells manifest revision number that corresponds to the given changeset. | 202 * Tells manifest revision number that corresponds to the given changeset. May return {@link HgRepository#BAD_REVISION} | 
| 198 * @return manifest revision index, or -1 if changeset has no associated manifest (cset records NULL nodeid for manifest) | 203 * if changeset has no associated manifest (cset records NULL nodeid for manifest). | 
| 204 * @return manifest revision index, non-negative, or {@link HgRepository#BAD_REVISION}. | |
| 205 * @throws HgInvalidRevisionException if method argument specifies non-existent revision index | |
| 206 * @throws IllegalArgumentException if argument is not a revision index | |
| 207 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | |
| 199 */ | 208 */ | 
| 200 /*package-local*/ int fromChangelog(int changesetRevisionIndex) throws HgInvalidControlFileException { | 209 /*package-local*/ int fromChangelog(int changesetRevisionIndex) throws HgInvalidRevisionException, HgInvalidControlFileException { | 
| 201 if (HgInternals.wrongRevisionIndex(changesetRevisionIndex)) { | 210 if (HgInternals.wrongRevisionIndex(changesetRevisionIndex)) { | 
| 202 throw new IllegalArgumentException(String.valueOf(changesetRevisionIndex)); | 211 throw new IllegalArgumentException(String.valueOf(changesetRevisionIndex)); | 
| 203 } | 212 } | 
| 204 if (changesetRevisionIndex == HgRepository.WORKING_COPY || changesetRevisionIndex == HgRepository.BAD_REVISION) { | 213 if (changesetRevisionIndex == HgRepository.WORKING_COPY || changesetRevisionIndex == HgRepository.BAD_REVISION) { | 
| 205 throw new IllegalArgumentException("Can't use constants like WORKING_COPY or BAD_REVISION"); | 214 throw new IllegalArgumentException("Can't use constants like WORKING_COPY or BAD_REVISION"); | 
| 216 * Extracts file revision as it was known at the time of given changeset. | 225 * Extracts file revision as it was known at the time of given changeset. | 
| 217 * | 226 * | 
| 218 * @param changelogRevisionIndex local changeset index | 227 * @param changelogRevisionIndex local changeset index | 
| 219 * @param file path to file in question | 228 * @param file path to file in question | 
| 220 * @return file revision or <code>null</code> if manifest at specified revision doesn't list such file | 229 * @return file revision or <code>null</code> if manifest at specified revision doesn't list such file | 
| 230 * @throws HgInvalidRevisionException if method argument specifies non-existent revision index | |
| 231 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | |
| 221 */ | 232 */ | 
| 222 @Experimental(reason="Perhaps, HgDataFile shall own this method, or get a delegate?") | 233 @Experimental(reason="Perhaps, HgDataFile shall own this method, or get a delegate?") | 
| 223 public Nodeid getFileRevision(int changelogRevisionIndex, final Path file) throws HgInvalidControlFileException{ | 234 public Nodeid getFileRevision(int changelogRevisionIndex, final Path file) throws HgInvalidRevisionException, HgInvalidControlFileException { | 
| 224 return getFileRevisions(file, changelogRevisionIndex).get(changelogRevisionIndex); | 235 return getFileRevisions(file, changelogRevisionIndex).get(changelogRevisionIndex); | 
| 225 } | 236 } | 
| 226 | 237 | 
| 227 // XXX package-local, IntMap, and HgDataFile getFileRevisionAt(int... localChangelogRevisions) | 238 // XXX package-local, IntMap, and HgDataFile getFileRevisionAt(int... localChangelogRevisions) | 
| 228 @Experimental(reason="@see #getFileRevision") | 239 @Experimental(reason="@see #getFileRevision") | 
| 229 public Map<Integer, Nodeid> getFileRevisions(final Path file, int... changelogRevisionIndexes) throws HgInvalidControlFileException{ | 240 public Map<Integer, Nodeid> getFileRevisions(final Path file, int... changelogRevisionIndexes) throws HgInvalidRevisionException, HgInvalidControlFileException { | 
| 230 // FIXME need tests | 241 // TODO need tests | 
| 231 int[] manifestRevisionIndexes = toManifestRevisionIndexes(changelogRevisionIndexes, null); | 242 int[] manifestRevisionIndexes = toManifestRevisionIndexes(changelogRevisionIndexes, null); | 
| 232 final HashMap<Integer,Nodeid> rv = new HashMap<Integer, Nodeid>(changelogRevisionIndexes.length); | 243 final HashMap<Integer,Nodeid> rv = new HashMap<Integer, Nodeid>(changelogRevisionIndexes.length); | 
| 233 content.iterate(manifestRevisionIndexes, true, new RevlogStream.Inspector() { | 244 content.iterate(manifestRevisionIndexes, true, new RevlogStream.Inspector() { | 
| 234 | 245 | 
| 235 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgException { | 246 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgException { | 
| 265 | 276 | 
| 266 | 277 | 
| 267 /** | 278 /** | 
| 268 * @param changelogRevisionIndexes non-null | 279 * @param changelogRevisionIndexes non-null | 
| 269 * @param inspector may be null if reporting of missing manifests is not needed | 280 * @param inspector may be null if reporting of missing manifests is not needed | 
| 281 * @throws HgInvalidRevisionException if arguments specify non-existent revision index | |
| 282 * @throws IllegalArgumentException if any index argument is not a revision index | |
| 283 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | |
| 270 */ | 284 */ | 
| 271 private int[] toManifestRevisionIndexes(int[] changelogRevisionIndexes, Inspector inspector) throws HgInvalidControlFileException { | 285 private int[] toManifestRevisionIndexes(int[] changelogRevisionIndexes, Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { | 
| 272 int[] manifestRevs = new int[changelogRevisionIndexes.length]; | 286 int[] manifestRevs = new int[changelogRevisionIndexes.length]; | 
| 273 boolean needsSort = false; | 287 boolean needsSort = false; | 
| 274 int j = 0; | 288 int j = 0; | 
| 275 for (int i = 0; i < changelogRevisionIndexes.length; i++) { | 289 for (int i = 0; i < changelogRevisionIndexes.length; i++) { | 
| 276 final int manifestRevisionIndex = fromChangelog(changelogRevisionIndexes[i]); | 290 final int manifestRevisionIndex = fromChangelog(changelogRevisionIndexes[i]); | 
| 277 if (manifestRevisionIndex == -1) { | 291 if (manifestRevisionIndex == BAD_REVISION) { | 
| 278 if (inspector != null) { | 292 if (inspector != null) { | 
| 279 inspector.begin(BAD_REVISION, NULL, changelogRevisionIndexes[i]); | 293 inspector.begin(BAD_REVISION, NULL, changelogRevisionIndexes[i]); | 
| 280 inspector.end(BAD_REVISION); | 294 inspector.end(BAD_REVISION); | 
| 281 } | 295 } | 
| 282 // othrwise, ignore changeset without manifest | 296 // othrwise, ignore changeset without manifest | 
| 496 } | 510 } | 
| 497 } | 511 } | 
| 498 | 512 | 
| 499 private static class RevisionMapper implements RevlogStream.Inspector, Lifecycle { | 513 private static class RevisionMapper implements RevlogStream.Inspector, Lifecycle { | 
| 500 | 514 | 
| 501 private final int changelogRevisions; | 515 private final int changelogRevisionCount; | 
| 502 private int[] changelog2manifest; | 516 private int[] changelog2manifest; | 
| 503 private final HgRepository repo; | 517 private final HgRepository repo; | 
| 504 | 518 | 
| 505 public RevisionMapper(HgRepository hgRepo) { | 519 public RevisionMapper(HgRepository hgRepo) { | 
| 506 repo = hgRepo; | 520 repo = hgRepo; | 
| 507 changelogRevisions = repo.getChangelog().getRevisionCount(); | 521 changelogRevisionCount = repo.getChangelog().getRevisionCount(); | 
| 508 } | 522 } | 
| 509 | 523 | 
| 510 // respects TIP | 524 /** | 
| 511 public int at(int revisionNumber) { | 525 * Get index of manifest revision that corresponds to specified changeset | 
| 512 if (revisionNumber == TIP) { | 526 * @param changesetRevisionIndex non-negative index of changelog revision, or {@link HgRepository#TIP} | 
| 513 revisionNumber = changelogRevisions - 1; | 527 * @return index of manifest revision, or {@link HgRepository#BAD_REVISION} if changeset doesn't reference a valid manifest | 
| 528 * @throws HgInvalidRevisionException if method argument specifies non-existent revision index | |
| 529 */ | |
| 530 public int at(int changesetRevisionIndex) throws HgInvalidRevisionException { | |
| 531 if (changesetRevisionIndex == TIP) { | |
| 532 changesetRevisionIndex = changelogRevisionCount - 1; | |
| 533 } | |
| 534 if (changesetRevisionIndex >= changelogRevisionCount) { | |
| 535 throw new HgInvalidRevisionException(changesetRevisionIndex); | |
| 514 } | 536 } | 
| 515 if (changelog2manifest != null) { | 537 if (changelog2manifest != null) { | 
| 516 return changelog2manifest[revisionNumber]; | 538 return changelog2manifest[changesetRevisionIndex]; | 
| 517 } | 539 } | 
| 518 return revisionNumber; | 540 return changesetRevisionIndex; | 
| 519 } | 541 } | 
| 520 | 542 | 
| 521 // XXX likely can be replaced with Revlog.RevisionInspector | 543 // XXX likely can be replaced with Revlog.RevisionInspector | 
| 522 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | 544 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | 
| 523 if (changelog2manifest != null) { | 545 if (changelog2manifest != null) { | 
| 524 // next assertion is not an error, rather assumption check, which is too development-related to be explicit exception - | 546 // next assertion is not an error, rather assumption check, which is too development-related to be explicit exception - | 
| 525 // I just wonder if there are manifests that have two entries pointing to single changeset. It seems unrealistic, though - | 547 // I just wonder if there are manifests that have two entries pointing to single changeset. It seems unrealistic, though - | 
| 526 // changeset records one and only one manifest nodeid | 548 // changeset records one and only one manifest nodeid | 
| 527 assert changelog2manifest[linkRevision] == -1 : String.format("revision:%d, link:%d, already linked to revision:%d", revisionNumber, linkRevision, changelog2manifest[linkRevision]); | 549 assert changelog2manifest[linkRevision] == BAD_REVISION : String.format("revision:%d, link:%d, already linked to revision:%d", revisionNumber, linkRevision, changelog2manifest[linkRevision]); | 
| 528 changelog2manifest[linkRevision] = revisionNumber; | 550 changelog2manifest[linkRevision] = revisionNumber; | 
| 529 } else { | 551 } else { | 
| 530 if (revisionNumber != linkRevision) { | 552 if (revisionNumber != linkRevision) { | 
| 531 changelog2manifest = new int[changelogRevisions]; | 553 changelog2manifest = new int[changelogRevisionCount]; | 
| 532 Arrays.fill(changelog2manifest, -1); | 554 Arrays.fill(changelog2manifest, BAD_REVISION); | 
| 533 for (int i = 0; i < revisionNumber; changelog2manifest[i] = i, i++) | 555 for (int i = 0; i < revisionNumber; changelog2manifest[i] = i, i++) | 
| 534 ; | 556 ; | 
| 535 changelog2manifest[linkRevision] = revisionNumber; | 557 changelog2manifest[linkRevision] = revisionNumber; | 
| 536 } | 558 } | 
| 537 } | 559 } | 
| 538 } | 560 } | 
| 539 | 561 | 
| 540 public void start(int count, Callback callback, Object token) { | 562 public void start(int count, Callback callback, Object token) { | 
| 541 if (count != changelogRevisions) { | 563 if (count != changelogRevisionCount) { | 
| 542 assert count < changelogRevisions; // no idea what to do if manifest has more revisions than changelog | 564 assert count < changelogRevisionCount; // no idea what to do if manifest has more revisions than changelog | 
| 543 // the way how manifest may contain more revisions than changelog, as I can imagine, is a result of | 565 // the way how manifest may contain more revisions than changelog, as I can imagine, is a result of | 
| 544 // some kind of an import tool (e.g. from SVN or CVS), that creates manifest and changelog independently. | 566 // some kind of an import tool (e.g. from SVN or CVS), that creates manifest and changelog independently. | 
| 545 // Note, it's pure guess, I didn't see such repository yet (although the way manifest revisions | 567 // Note, it's pure guess, I didn't see such repository yet (although the way manifest revisions | 
| 546 // in cpython repo are numbered makes me think aforementioned way) | 568 // in cpython repo are numbered makes me think aforementioned way) | 
| 547 changelog2manifest = new int[changelogRevisions]; | 569 changelog2manifest = new int[changelogRevisionCount]; | 
| 548 Arrays.fill(changelog2manifest, -1); | 570 Arrays.fill(changelog2manifest, BAD_REVISION); | 
| 549 } | 571 } | 
| 550 } | 572 } | 
| 551 | 573 | 
| 552 public void finish(Object token) { | 574 public void finish(Object token) { | 
| 553 if (changelog2manifest == null) { | 575 if (changelog2manifest == null) { | 
| 554 return; | 576 return; | 
| 555 } | 577 } | 
| 556 // I assume there'd be not too many revisions we don't know manifest of | 578 // I assume there'd be not too many revisions we don't know manifest of | 
| 557 ArrayList<Integer> undefinedChangelogRevision = new ArrayList<Integer>(); | 579 ArrayList<Integer> undefinedChangelogRevision = new ArrayList<Integer>(); | 
| 558 for (int i = 0; i < changelog2manifest.length; i++) { | 580 for (int i = 0; i < changelog2manifest.length; i++) { | 
| 559 if (changelog2manifest[i] == -1) { | 581 if (changelog2manifest[i] == BAD_REVISION) { | 
| 560 undefinedChangelogRevision.add(i); | 582 undefinedChangelogRevision.add(i); | 
| 561 } | 583 } | 
| 562 } | 584 } | 
| 563 for (int u : undefinedChangelogRevision) { | 585 for (int u : undefinedChangelogRevision) { | 
| 564 try { | 586 try { | 
