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 }