Mercurial > hg4j
comparison src/org/tmatesoft/hg/repo/HgManifest.java @ 423:9c9c442b5f2e
Major refactoring of exception handling. Low-level API uses RuntimeExceptions, while checked are left for higher level
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 23 Mar 2012 22:51:18 +0100 |
| parents | fdd7d756dea0 |
| children | 6437d261048a |
comparison
equal
deleted
inserted
replaced
| 422:5d1cc7366d04 | 423:9c9c442b5f2e |
|---|---|
| 15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com |
| 16 */ | 16 */ |
| 17 package org.tmatesoft.hg.repo; | 17 package org.tmatesoft.hg.repo; |
| 18 | 18 |
| 19 import static org.tmatesoft.hg.core.Nodeid.NULL; | 19 import static org.tmatesoft.hg.core.Nodeid.NULL; |
| 20 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION; | 20 import static org.tmatesoft.hg.repo.HgRepository.*; |
| 21 import static org.tmatesoft.hg.repo.HgRepository.TIP; | |
| 22 | 21 |
| 23 import java.io.ByteArrayOutputStream; | 22 import java.io.ByteArrayOutputStream; |
| 24 import java.io.IOException; | 23 import java.io.IOException; |
| 25 import java.util.ArrayList; | 24 import java.util.ArrayList; |
| 26 import java.util.Arrays; | 25 import java.util.Arrays; |
| 27 import java.util.HashMap; | 26 import java.util.HashMap; |
| 28 import java.util.Map; | 27 import java.util.Map; |
| 29 | 28 |
| 30 import org.tmatesoft.hg.core.HgBadStateException; | |
| 31 import org.tmatesoft.hg.core.HgChangesetFileSneaker; | 29 import org.tmatesoft.hg.core.HgChangesetFileSneaker; |
| 32 import org.tmatesoft.hg.core.HgException; | |
| 33 import org.tmatesoft.hg.core.HgInvalidControlFileException; | |
| 34 import org.tmatesoft.hg.core.HgInvalidRevisionException; | |
| 35 import org.tmatesoft.hg.core.Nodeid; | 30 import org.tmatesoft.hg.core.Nodeid; |
| 31 import org.tmatesoft.hg.internal.Callback; | |
| 36 import org.tmatesoft.hg.internal.DataAccess; | 32 import org.tmatesoft.hg.internal.DataAccess; |
| 37 import org.tmatesoft.hg.internal.DigestHelper; | 33 import org.tmatesoft.hg.internal.DigestHelper; |
| 38 import org.tmatesoft.hg.internal.EncodingHelper; | 34 import org.tmatesoft.hg.internal.EncodingHelper; |
| 39 import org.tmatesoft.hg.internal.Experimental; | 35 import org.tmatesoft.hg.internal.Experimental; |
| 40 import org.tmatesoft.hg.internal.IntMap; | 36 import org.tmatesoft.hg.internal.IntMap; |
| 135 * | 131 * |
| 136 * <p>Physical layout of mercurial files (revlog) doesn't impose any restriction on whether manifest and changeset revisions shall go | 132 * <p>Physical layout of mercurial files (revlog) doesn't impose any restriction on whether manifest and changeset revisions shall go |
| 137 * incrementally, nor it mandates presence of manifest version for a changeset. Thus, there might be changesets that record {@link Nodeid#NULL} | 133 * incrementally, nor it mandates presence of manifest version for a changeset. Thus, there might be changesets that record {@link Nodeid#NULL} |
| 138 * as corresponding manifest revision. This situation is deemed exceptional now and what would <code>inspector</code> get depends on whether | 134 * as corresponding manifest revision. This situation is deemed exceptional now and what would <code>inspector</code> get depends on whether |
| 139 * <code>start</code> or <code>end</code> arguments point to such changeset, or such changeset happen to be somewhere inside the range | 135 * <code>start</code> or <code>end</code> arguments point to such changeset, or such changeset happen to be somewhere inside the range |
| 140 * <code>[start..end]</code>. Implementation does it best to report empty manifests (<code>Inspector.begin(BAD_REVISION, NULL, csetRevIndex);</code> | 136 * <code>[start..end]</code>. Implementation does it best to report empty manifests |
| 141 * followed immediately by <code>Inspector.end(BAD_REVISION)</code> when <code>start</code> and/or <code>end</code> point to changeset with no associated | 137 * (<code>Inspector.begin(HgRepository.NO_REVISION, NULL, csetRevIndex);</code> |
| 138 * followed immediately by <code>Inspector.end(HgRepository.NO_REVISION)</code> | |
| 139 * when <code>start</code> and/or <code>end</code> point to changeset with no associated | |
| 142 * manifest revision. However, if changeset-manifest revision pairs look like: | 140 * manifest revision. However, if changeset-manifest revision pairs look like: |
| 143 * <pre> | 141 * <pre> |
| 144 * 3 8 | 142 * 3 8 |
| 145 * 4 -1 (cset records null revision for manifest) | 143 * 4 -1 (cset records null revision for manifest) |
| 146 * 5 9 | 144 * 5 9 |
| 147 * </pre> | 145 * </pre> |
| 148 * call <code>walk(3,5, insp)</code> would yield only (3,8) and (5,9) to the inspector, without additional empty | 146 * call <code>walk(3,5, insp)</code> would yield only (3,8) and (5,9) to the inspector, without additional empty |
| 149 * <code>Inspector.begin(); Inspector.end()</code> call pair. | 147 * <code>Inspector.begin(); Inspector.end()</code> call pair. |
| 150 * | 148 * |
| 149 * @see HgRepository#NO_REVISION | |
| 151 * @param start changelog (not manifest!) revision to begin with | 150 * @param start changelog (not manifest!) revision to begin with |
| 152 * @param end changelog (not manifest!) revision to stop, inclusive. | 151 * @param end changelog (not manifest!) revision to stop, inclusive. |
| 153 * @param inspector manifest revision visitor, can't be <code>null</code> | 152 * @param inspector manifest revision visitor, can't be <code>null</code> |
| 154 * @throws HgInvalidRevisionException if start or end specify non-existent revision index | 153 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
| 155 * @throws IllegalArgumentException if start or end is not a revision index | 154 * @throws IllegalArgumentException if inspector callback is <code>null</code> |
| 156 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | 155 */ |
| 157 */ | 156 public void walk(int start, int end, final Inspector inspector) throws HgRuntimeException, IllegalArgumentException { |
| 158 public void walk(int start, int end, final Inspector inspector) throws HgInvalidRevisionException, HgInvalidControlFileException { | |
| 159 if (inspector == null) { | 157 if (inspector == null) { |
| 160 throw new IllegalArgumentException(); | 158 throw new IllegalArgumentException(); |
| 161 } | 159 } |
| 162 final int csetFirst = start <= end ? start : end, csetLast = start > end ? start : end; | 160 final int csetFirst = start <= end ? start : end, csetLast = start > end ? start : end; |
| 163 int manifestFirst, manifestLast, i = 0; | 161 int manifestFirst, manifestLast, i = 0; |
| 164 do { | 162 do { |
| 165 manifestFirst = fromChangelog(csetFirst+i); | 163 manifestFirst = fromChangelog(csetFirst+i); |
| 166 if (manifestFirst == BAD_REVISION) { | 164 if (manifestFirst == BAD_REVISION) { |
| 167 inspector.begin(BAD_REVISION, NULL, csetFirst+i); | 165 inspector.begin(NO_REVISION, NULL, csetFirst+i); |
| 168 inspector.end(BAD_REVISION); | 166 inspector.end(NO_REVISION); |
| 169 } | 167 } |
| 170 i++; | 168 i++; |
| 171 } while (manifestFirst == BAD_REVISION && csetFirst+i <= csetLast); | 169 } while (manifestFirst == BAD_REVISION && csetFirst+i <= csetLast); |
| 172 if (manifestFirst == BAD_REVISION) { | 170 if (manifestFirst == BAD_REVISION) { |
| 173 getRepo().getContext().getLog().info(getClass(), "None of changesets [%d..%d] have associated manifest revision", csetFirst, csetLast); | 171 getRepo().getContext().getLog().info(getClass(), "None of changesets [%d..%d] have associated manifest revision", csetFirst, csetLast); |
| 177 } | 175 } |
| 178 i = 0; | 176 i = 0; |
| 179 do { | 177 do { |
| 180 manifestLast = fromChangelog(csetLast-i); | 178 manifestLast = fromChangelog(csetLast-i); |
| 181 if (manifestLast == BAD_REVISION) { | 179 if (manifestLast == BAD_REVISION) { |
| 182 inspector.begin(BAD_REVISION, NULL, csetLast-i); | 180 inspector.begin(NO_REVISION, NULL, csetLast-i); |
| 183 inspector.end(BAD_REVISION); | 181 inspector.end(NO_REVISION); |
| 184 } | 182 } |
| 185 i++; | 183 i++; |
| 186 } while (manifestLast == BAD_REVISION && csetLast-i >= csetFirst); | 184 } while (manifestLast == BAD_REVISION && csetLast-i >= csetFirst); |
| 187 if (manifestLast == BAD_REVISION) { | 185 if (manifestLast == BAD_REVISION) { |
| 188 // hmm, manifestFirst != -1 here, hence there's i from [csetFirst..csetLast] for which manifest entry exists, | 186 // hmm, manifestFirst != BAD_REVISION here, hence there's i from [csetFirst..csetLast] for which manifest entry exists, |
| 189 // and thus it's impossible to run into manifestLast == -1. Nevertheless, never hurts to check. | 187 // and thus it's impossible to run into manifestLast == BAD_REVISION. Nevertheless, never hurts to check. |
| 190 throw new HgBadStateException(String.format("Manifest %d-%d(!) for cset range [%d..%d] ", manifestFirst, manifestLast, csetFirst, csetLast)); | 188 throw new HgInvalidStateException(String.format("Manifest %d-%d(!) for cset range [%d..%d] ", manifestFirst, manifestLast, csetFirst, csetLast)); |
| 191 } | 189 } |
| 192 if (manifestLast < manifestFirst) { | 190 if (manifestLast < manifestFirst) { |
| 193 // there are tool-constructed repositories that got order of changeset revisions completely different from that of manifest | 191 // there are tool-constructed repositories that got order of changeset revisions completely different from that of manifest |
| 194 int x = manifestLast; | 192 int x = manifestLast; |
| 195 manifestLast = manifestFirst; | 193 manifestLast = manifestFirst; |
| 205 * gets invoked doesn't resemble order of changeset revisions supplied, manifest revisions are reported in the order they appear | 203 * gets invoked doesn't resemble order of changeset revisions supplied, manifest revisions are reported in the order they appear |
| 206 * in manifest revlog (with exception of changesets with missing manifest that may be reported in any order). | 204 * in manifest revlog (with exception of changesets with missing manifest that may be reported in any order). |
| 207 * | 205 * |
| 208 * @param inspector manifest revision visitor, can't be <code>null</code> | 206 * @param inspector manifest revision visitor, can't be <code>null</code> |
| 209 * @param revisionIndexes local indexes of changesets to visit, non-<code>null</code> | 207 * @param revisionIndexes local indexes of changesets to visit, non-<code>null</code> |
| 210 * @throws HgInvalidRevisionException if argument specifies non-existent revision index | 208 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
| 211 * @throws HgInvalidControlFileException if access to revlog index/data entry failed | 209 * @throws InvalidArgumentException if supplied arguments are <code>null</code>s |
| 212 */ | 210 */ |
| 213 public void walk(final Inspector inspector, int... revisionIndexes) throws HgInvalidRevisionException, HgInvalidControlFileException { | 211 public void walk(final Inspector inspector, int... revisionIndexes) throws HgRuntimeException, IllegalArgumentException { |
| 214 if (inspector == null || revisionIndexes == null) { | 212 if (inspector == null || revisionIndexes == null) { |
| 215 throw new IllegalArgumentException(); | 213 throw new IllegalArgumentException(); |
| 216 } | 214 } |
| 217 int[] manifestRevs = toManifestRevisionIndexes(revisionIndexes, inspector); | 215 int[] manifestRevs = toManifestRevisionIndexes(revisionIndexes, inspector); |
| 218 content.iterate(manifestRevs, true, new ManifestParser(inspector, encodingHelper)); | 216 content.iterate(manifestRevs, true, new ManifestParser(inspector, encodingHelper)); |
| 308 int j = 0; | 306 int j = 0; |
| 309 for (int i = 0; i < changelogRevisionIndexes.length; i++) { | 307 for (int i = 0; i < changelogRevisionIndexes.length; i++) { |
| 310 final int manifestRevisionIndex = fromChangelog(changelogRevisionIndexes[i]); | 308 final int manifestRevisionIndex = fromChangelog(changelogRevisionIndexes[i]); |
| 311 if (manifestRevisionIndex == BAD_REVISION) { | 309 if (manifestRevisionIndex == BAD_REVISION) { |
| 312 if (inspector != null) { | 310 if (inspector != null) { |
| 313 inspector.begin(BAD_REVISION, NULL, changelogRevisionIndexes[i]); | 311 inspector.begin(NO_REVISION, NULL, changelogRevisionIndexes[i]); |
| 314 inspector.end(BAD_REVISION); | 312 inspector.end(NO_REVISION); |
| 315 } | 313 } |
| 316 // othrwise, ignore changeset without manifest | 314 // othrwise, ignore changeset without manifest |
| 317 } else { | 315 } else { |
| 318 manifestRevs[j] = manifestRevisionIndex; | 316 manifestRevs[j] = manifestRevisionIndex; |
| 319 if (j > 0 && manifestRevs[j-1] > manifestRevisionIndex) { | 317 if (j > 0 && manifestRevs[j-1] > manifestRevisionIndex) { |
| 333 System.arraycopy(manifestRevs, 0, rv, 0, j); | 331 System.arraycopy(manifestRevs, 0, rv, 0, j); |
| 334 return rv; | 332 return rv; |
| 335 } | 333 } |
| 336 } | 334 } |
| 337 | 335 |
| 336 @Callback | |
| 338 public interface Inspector { | 337 public interface Inspector { |
| 339 boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision); | 338 boolean begin(int mainfestRevision, Nodeid nid, int changelogRevision); |
| 340 /** | 339 /** |
| 341 * @deprecated switch to {@link HgManifest.Inspector2#next(Nodeid, Path, HgManifest.Flags)} | 340 * @deprecated switch to {@link HgManifest.Inspector2#next(Nodeid, Path, HgManifest.Flags)} |
| 342 */ | 341 */ |
| 344 boolean next(Nodeid nid, String fname, String flags); | 343 boolean next(Nodeid nid, String fname, String flags); |
| 345 boolean end(int manifestRevision); | 344 boolean end(int manifestRevision); |
| 346 } | 345 } |
| 347 | 346 |
| 348 @Experimental(reason="Explore Path alternative for filenames and enum for flags") | 347 @Experimental(reason="Explore Path alternative for filenames and enum for flags") |
| 348 @Callback | |
| 349 public interface Inspector2 extends Inspector { | 349 public interface Inspector2 extends Inspector { |
| 350 /** | 350 /** |
| 351 * @param nid file revision | 351 * @param nid file revision |
| 352 * @param fname file name | 352 * @param fname file name |
| 353 * @param flags one of {@link HgManifest.Flags} constants, not <code>null</code> | 353 * @param flags one of {@link HgManifest.Flags} constants, not <code>null</code> |
| 446 fnamePool = new Pool2<PathProxy>(); | 446 fnamePool = new Pool2<PathProxy>(); |
| 447 thisRevPool = new Pool2<Nodeid>(); | 447 thisRevPool = new Pool2<Nodeid>(); |
| 448 progressHelper = ProgressSupport.Factory.get(delegate); | 448 progressHelper = ProgressSupport.Factory.get(delegate); |
| 449 } | 449 } |
| 450 | 450 |
| 451 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) throws HgException { | 451 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess da) { |
| 452 try { | 452 try { |
| 453 if (!inspector.begin(revisionNumber, new Nodeid(nodeid, true), linkRevision)) { | 453 if (!inspector.begin(revisionNumber, new Nodeid(nodeid, true), linkRevision)) { |
| 454 iterateControl.stop(); | 454 iterateControl.stop(); |
| 455 return; | 455 return; |
| 456 } | 456 } |
| 523 nodeidPool = thisRevPool; | 523 nodeidPool = thisRevPool; |
| 524 thisRevPool = t; | 524 thisRevPool = t; |
| 525 iterateControl.checkCancelled(); | 525 iterateControl.checkCancelled(); |
| 526 progressHelper.worked(1); | 526 progressHelper.worked(1); |
| 527 } catch (IOException ex) { | 527 } catch (IOException ex) { |
| 528 throw new HgException(ex); | 528 throw new HgInvalidControlFileException("Failed reading manifest", ex, null).setRevisionIndex(revisionNumber); |
| 529 } | 529 } |
| 530 } | 530 } |
| 531 | 531 |
| 532 public void start(int count, Callback callback, Object token) { | 532 public void start(int count, Callback callback, Object token) { |
| 533 CancelSupport cs = CancelSupport.Factory.get(inspector, null); | 533 CancelSupport cs = CancelSupport.Factory.get(inspector, null); |
| 611 if (changelog2manifest[i] == BAD_REVISION) { | 611 if (changelog2manifest[i] == BAD_REVISION) { |
| 612 undefinedChangelogRevision.add(i); | 612 undefinedChangelogRevision.add(i); |
| 613 } | 613 } |
| 614 } | 614 } |
| 615 for (int u : undefinedChangelogRevision) { | 615 for (int u : undefinedChangelogRevision) { |
| 616 try { | 616 Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest(); |
| 617 Nodeid manifest = repo.getChangelog().range(u, u).get(0).manifest(); | 617 // TODO calculate those missing effectively (e.g. cache and sort nodeids to speed lookup |
| 618 // TODO calculate those missing effectively (e.g. cache and sort nodeids to speed lookup | 618 // right away in the #next (may refactor ParentWalker's sequential and sorted into dedicated helper and reuse here) |
| 619 // right away in the #next (may refactor ParentWalker's sequential and sorted into dedicated helper and reuse here) | 619 if (manifest.isNull()) { |
| 620 if (manifest.isNull()) { | 620 repo.getContext().getLog().warn(getClass(), "Changeset %d has no associated manifest entry", u); |
| 621 repo.getContext().getLog().warn(getClass(), "Changeset %d has no associated manifest entry", u); | 621 // keep -1 in the changelog2manifest map. |
| 622 // keep -1 in the changelog2manifest map. | 622 } else { |
| 623 } else { | 623 changelog2manifest[u] = repo.getManifest().getRevisionIndex(manifest); |
| 624 changelog2manifest[u] = repo.getManifest().getRevisionIndex(manifest); | |
| 625 } | |
| 626 } catch (HgInvalidControlFileException ex) { | |
| 627 // FIXME EXCEPTIONS need to propagate the error up to client | |
| 628 repo.getContext().getLog().error(getClass(), ex, null); | |
| 629 } | 624 } |
| 630 } | 625 } |
| 631 } | 626 } |
| 632 } | 627 } |
| 633 | 628 |
| 647 csetIndex2FileRev = csetIndex2FileRevMap; | 642 csetIndex2FileRev = csetIndex2FileRevMap; |
| 648 csetIndex2Flags = csetIndex2FlagsMap; | 643 csetIndex2Flags = csetIndex2FlagsMap; |
| 649 filenameAsBytes = eh.toManifest(fileToLookUp.toString()); | 644 filenameAsBytes = eh.toManifest(fileToLookUp.toString()); |
| 650 } | 645 } |
| 651 | 646 |
| 652 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) throws HgException { | 647 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { |
| 653 ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 648 ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| 654 try { | 649 try { |
| 655 byte b; | 650 byte b; |
| 656 while (!data.isEmpty() && (b = data.readByte()) != '\n') { | 651 while (!data.isEmpty() && (b = data.readByte()) != '\n') { |
| 657 if (b != 0) { | 652 if (b != 0) { |
| 687 while (!data.isEmpty() && (b = data.readByte()) != '\n') | 682 while (!data.isEmpty() && (b = data.readByte()) != '\n') |
| 688 ; | 683 ; |
| 689 } | 684 } |
| 690 } | 685 } |
| 691 } catch (IOException ex) { | 686 } catch (IOException ex) { |
| 692 throw new HgException(ex); // FIXME EXCEPTIONS | 687 throw new HgInvalidControlFileException("Failed reading manifest", ex, null); |
| 693 } | 688 } |
| 694 } | 689 } |
| 695 } | 690 } |
| 696 } | 691 } |
