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