Mercurial > jhg
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 { |
