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