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 {