Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgManifest.java @ 426:063b0663495a
HgManifest#getFileRevisions refactored into #walkFileRevisions to match pattern throught rest of the library
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Wed, 28 Mar 2012 19:34:37 +0200 |
| parents | 48f993aa2f41 |
| children | 12f668401613 |
comparison
equal
deleted
inserted
replaced
| 425:48f993aa2f41 | 426:063b0663495a |
|---|---|
| 21 | 21 |
| 22 import java.io.ByteArrayOutputStream; | 22 import java.io.ByteArrayOutputStream; |
| 23 import java.io.IOException; | 23 import java.io.IOException; |
| 24 import java.util.ArrayList; | 24 import java.util.ArrayList; |
| 25 import java.util.Arrays; | 25 import java.util.Arrays; |
| 26 import java.util.HashMap; | |
| 27 import java.util.Map; | |
| 28 | 26 |
| 29 import org.tmatesoft.hg.core.HgChangesetFileSneaker; | 27 import org.tmatesoft.hg.core.HgChangesetFileSneaker; |
| 30 import org.tmatesoft.hg.core.Nodeid; | 28 import org.tmatesoft.hg.core.Nodeid; |
| 31 import org.tmatesoft.hg.internal.Callback; | 29 import org.tmatesoft.hg.internal.Callback; |
| 32 import org.tmatesoft.hg.internal.DataAccess; | 30 import org.tmatesoft.hg.internal.DataAccess; |
| 33 import org.tmatesoft.hg.internal.DigestHelper; | 31 import org.tmatesoft.hg.internal.DigestHelper; |
| 34 import org.tmatesoft.hg.internal.EncodingHelper; | 32 import org.tmatesoft.hg.internal.EncodingHelper; |
| 35 import org.tmatesoft.hg.internal.Experimental; | |
| 36 import org.tmatesoft.hg.internal.IntMap; | 33 import org.tmatesoft.hg.internal.IntMap; |
| 37 import org.tmatesoft.hg.internal.IterateControlMediator; | 34 import org.tmatesoft.hg.internal.IterateControlMediator; |
| 38 import org.tmatesoft.hg.internal.Lifecycle; | 35 import org.tmatesoft.hg.internal.Lifecycle; |
| 39 import org.tmatesoft.hg.internal.Pool2; | 36 import org.tmatesoft.hg.internal.Pool2; |
| 40 import org.tmatesoft.hg.internal.RevlogStream; | 37 import org.tmatesoft.hg.internal.RevlogStream; |
| 48 * | 45 * |
| 49 * @see http://mercurial.selenic.com/wiki/Manifest | 46 * @see http://mercurial.selenic.com/wiki/Manifest |
| 50 * @author Artem Tikhomirov | 47 * @author Artem Tikhomirov |
| 51 * @author TMate Software Ltd. | 48 * @author TMate Software Ltd. |
| 52 */ | 49 */ |
| 53 public class HgManifest extends Revlog { | 50 public final class HgManifest extends Revlog { |
| 54 private RevisionMapper revisionMap; | 51 private RevisionMapper revisionMap; |
| 55 private EncodingHelper encodingHelper; | 52 private EncodingHelper encodingHelper; |
| 56 | 53 |
| 57 /** | 54 /** |
| 58 * File flags recorded in manifest | 55 * File flags recorded in manifest |
| 241 return revisionMap.at(changesetRevisionIndex); | 238 return revisionMap.at(changesetRevisionIndex); |
| 242 } | 239 } |
| 243 | 240 |
| 244 /** | 241 /** |
| 245 * Extracts file revision as it was known at the time of given changeset. | 242 * Extracts file revision as it was known at the time of given changeset. |
| 246 * For more thorough details about file at specific changeset, use {@link HgChangesetFileSneaker}. | 243 * <p>For more thorough details about file at specific changeset, use {@link HgChangesetFileSneaker}. |
| 244 * <p>To visit few changesets for the same file, use {@link #walkFileRevisions(Path, Inspector, int...)} | |
| 247 * | 245 * |
| 246 * @see #walkFileRevisions(Path, Inspector, int...) | |
| 248 * @see HgChangesetFileSneaker | 247 * @see HgChangesetFileSneaker |
| 249 * @param changelogRevisionIndex local changeset index | 248 * @param changelogRevisionIndex local changeset index |
| 250 * @param file path to file in question | 249 * @param file path to file in question |
| 251 * @return file revision or <code>null</code> if manifest at specified revision doesn't list such file | 250 * @return file revision or <code>null</code> if manifest at specified revision doesn't list such file |
| 252 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> | 251 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
| 255 // there's no need for HgDataFile to own this method, or get a delegate | 254 // there's no need for HgDataFile to own this method, or get a delegate |
| 256 // as most of HgDataFile API is using file revision indexes, and there's easy step from file revision index to | 255 // as most of HgDataFile API is using file revision indexes, and there's easy step from file revision index to |
| 257 // both file revision and changeset revision index. But there's no easy way to go from changesetRevisionIndex to | 256 // both file revision and changeset revision index. But there's no easy way to go from changesetRevisionIndex to |
| 258 // file revision (the task this method solves), exept for HgFileInformer | 257 // file revision (the task this method solves), exept for HgFileInformer |
| 259 // I feel methods dealing with changeset indexes shall be more exposed in HgChangelog and HgManifest API. | 258 // I feel methods dealing with changeset indexes shall be more exposed in HgChangelog and HgManifest API. |
| 260 return getFileRevisions(file, changelogRevisionIndex).get(changelogRevisionIndex); | |
| 261 } | |
| 262 | |
| 263 // XXX package-local or better API | |
| 264 @Experimental(reason="Map as return value isn't that good") | |
| 265 public Map<Integer, Nodeid> getFileRevisions(final Path file, int... changelogRevisionIndexes) throws HgInvalidRevisionException, HgInvalidControlFileException { | |
| 266 // FIXME in fact, walk(Inspectr, path, int[]) might be better alternative than get() | |
| 267 // TODO need tests | 259 // TODO need tests |
| 268 int[] manifestRevisionIndexes = toManifestRevisionIndexes(changelogRevisionIndexes, null); | 260 int manifestRevIndex = fromChangelog(changelogRevisionIndex); |
| 269 IntMap<Nodeid> resMap = new IntMap<Nodeid>(changelogRevisionIndexes.length); | 261 if (manifestRevIndex == BAD_REVISION) { |
| 270 content.iterate(manifestRevisionIndexes, true, new FileLookupInspector(encodingHelper, file, resMap, null)); | 262 return null; |
| 271 // IntMap to HashMap, | 263 } |
| 272 HashMap<Integer,Nodeid> rv = new HashMap<Integer, Nodeid>(); | 264 IntMap<Nodeid> resMap = new IntMap<Nodeid>(3); |
| 273 resMap.fill(rv); | 265 FileLookupInspector parser = new FileLookupInspector(encodingHelper, file, resMap, null); |
| 274 return rv; | 266 parser.walk(manifestRevIndex, content); |
| 267 return resMap.get(changelogRevisionIndex); | |
| 268 } | |
| 269 | |
| 270 /** | |
| 271 * Visit file revisions as they were recorded at the time of given changesets. Same file revision may be reported as many times as | |
| 272 * there are changesets that refer to that revision. Both {@link Inspector#begin(int, Nodeid, int)} and {@link Inspector#end(int)} | |
| 273 * with appropriate values are invoked around {@link Inspector#next(Nodeid, Path, Flags)} call for the supplied file | |
| 274 * | |
| 275 * <p>NOTE, this method doesn't respect return values from callback (i.e. to stop iteration), as it's lookup of a single file | |
| 276 * and canceling it seems superfluous. However, this may change in future and it's recommended to return <code>true</code> from | |
| 277 * all {@link Inspector} methods. | |
| 278 * | |
| 279 * @see #getFileRevision(int, Path) | |
| 280 * @param file path of interest | |
| 281 * @param inspector callback to receive details about selected file | |
| 282 * @param changelogRevisionIndexes changeset indexes to visit | |
| 283 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> | |
| 284 */ | |
| 285 public void walkFileRevisions(Path file, Inspector inspector, int... changelogRevisionIndexes) throws HgRuntimeException { | |
| 286 if (file == null || inspector == null || changelogRevisionIndexes == null) { | |
| 287 throw new IllegalArgumentException(); | |
| 288 } | |
| 289 // TODO [post-1.0] need tests. There's Main#checkWalkFileRevisions that may be a starting point | |
| 290 int[] manifestRevIndexes = toManifestRevisionIndexes(changelogRevisionIndexes, null); | |
| 291 FileLookupInspector parser = new FileLookupInspector(encodingHelper, file, inspector); | |
| 292 parser.walk(manifestRevIndexes, content); | |
| 275 } | 293 } |
| 276 | 294 |
| 277 /** | 295 /** |
| 278 * Extract file {@link Flags flags} as they were recorded in appropriate manifest version. | 296 * Extract file {@link Flags flags} as they were recorded in appropriate manifest version. |
| 279 * | 297 * |
| 284 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> | 302 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
| 285 */ | 303 */ |
| 286 public Flags getFileFlags(int changesetRevIndex, Path file) throws HgInvalidRevisionException, HgInvalidControlFileException { | 304 public Flags getFileFlags(int changesetRevIndex, Path file) throws HgInvalidRevisionException, HgInvalidControlFileException { |
| 287 int manifestRevIdx = fromChangelog(changesetRevIndex); | 305 int manifestRevIdx = fromChangelog(changesetRevIndex); |
| 288 IntMap<Flags> resMap = new IntMap<Flags>(2); | 306 IntMap<Flags> resMap = new IntMap<Flags>(2); |
| 289 content.iterate(manifestRevIdx, manifestRevIdx, true, new FileLookupInspector(encodingHelper, file, null, resMap)); | 307 FileLookupInspector parser = new FileLookupInspector(encodingHelper, file, null, resMap); |
| 308 parser.walk(manifestRevIdx, content); | |
| 290 return resMap.get(changesetRevIndex); | 309 return resMap.get(changesetRevIndex); |
| 291 } | 310 } |
| 292 | 311 |
| 293 | 312 |
| 294 /** | 313 /** |
| 609 /** | 628 /** |
| 610 * Look up specified file in possibly multiple manifest revisions, collect file revision and flags. | 629 * Look up specified file in possibly multiple manifest revisions, collect file revision and flags. |
| 611 */ | 630 */ |
| 612 private static class FileLookupInspector implements RevlogStream.Inspector { | 631 private static class FileLookupInspector implements RevlogStream.Inspector { |
| 613 | 632 |
| 633 private final Path filename; | |
| 614 private final byte[] filenameAsBytes; | 634 private final byte[] filenameAsBytes; |
| 615 private final IntMap<Nodeid> csetIndex2FileRev; | 635 private final IntMap<Nodeid> csetIndex2FileRev; |
| 616 private final IntMap<Flags> csetIndex2Flags; | 636 private final IntMap<Flags> csetIndex2Flags; |
| 637 private final Inspector delegate; | |
| 617 | 638 |
| 618 public FileLookupInspector(EncodingHelper eh, Path fileToLookUp, IntMap<Nodeid> csetIndex2FileRevMap, IntMap<Flags> csetIndex2FlagsMap) { | 639 public FileLookupInspector(EncodingHelper eh, Path fileToLookUp, IntMap<Nodeid> csetIndex2FileRevMap, IntMap<Flags> csetIndex2FlagsMap) { |
| 619 assert fileToLookUp != null; | 640 assert fileToLookUp != null; |
| 620 // need at least one map for the inspector to make any sense | 641 // need at least one map for the inspector to make any sense |
| 621 assert csetIndex2FileRevMap != null || csetIndex2FlagsMap != null; | 642 assert csetIndex2FileRevMap != null || csetIndex2FlagsMap != null; |
| 643 filename = fileToLookUp; | |
| 644 filenameAsBytes = eh.toManifest(fileToLookUp.toString()); | |
| 645 delegate = null; | |
| 622 csetIndex2FileRev = csetIndex2FileRevMap; | 646 csetIndex2FileRev = csetIndex2FileRevMap; |
| 623 csetIndex2Flags = csetIndex2FlagsMap; | 647 csetIndex2Flags = csetIndex2FlagsMap; |
| 648 } | |
| 649 | |
| 650 public FileLookupInspector(EncodingHelper eh, Path fileToLookUp, Inspector delegateInspector) { | |
| 651 assert fileToLookUp != null; | |
| 652 assert delegateInspector != null; | |
| 653 filename = fileToLookUp; | |
| 624 filenameAsBytes = eh.toManifest(fileToLookUp.toString()); | 654 filenameAsBytes = eh.toManifest(fileToLookUp.toString()); |
| 655 delegate = delegateInspector; | |
| 656 csetIndex2FileRev = null; | |
| 657 csetIndex2Flags = null; | |
| 658 } | |
| 659 | |
| 660 void walk(int manifestRevIndex, RevlogStream content) { | |
| 661 content.iterate(manifestRevIndex, manifestRevIndex, true, this); | |
| 662 } | |
| 663 | |
| 664 void walk(int[] manifestRevIndexes, RevlogStream content) { | |
| 665 content.iterate(manifestRevIndexes, true, this); | |
| 625 } | 666 } |
| 626 | 667 |
| 627 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { | 668 public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, DataAccess data) { |
| 628 ByteArrayOutputStream bos = new ByteArrayOutputStream(); | 669 ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| 629 try { | 670 try { |
| 633 bos.write(b); | 674 bos.write(b); |
| 634 } else { | 675 } else { |
| 635 byte[] byteArray = bos.toByteArray(); | 676 byte[] byteArray = bos.toByteArray(); |
| 636 bos.reset(); | 677 bos.reset(); |
| 637 if (Arrays.equals(filenameAsBytes, byteArray)) { | 678 if (Arrays.equals(filenameAsBytes, byteArray)) { |
| 638 if (csetIndex2FileRev != null) { | 679 Nodeid fileRev = null; |
| 680 Flags flags = null; | |
| 681 if (csetIndex2FileRev != null || delegate != null) { | |
| 639 byte[] nid = new byte[40]; | 682 byte[] nid = new byte[40]; |
| 640 data.readBytes(nid, 0, 40); | 683 data.readBytes(nid, 0, 40); |
| 641 csetIndex2FileRev.put(linkRevision, Nodeid.fromAscii(nid, 0, 40)); | 684 fileRev = Nodeid.fromAscii(nid, 0, 40); |
| 642 } else { | 685 } else { |
| 643 data.skip(40); | 686 data.skip(40); |
| 644 } | 687 } |
| 645 if (csetIndex2Flags != null) { | 688 if (csetIndex2Flags != null || delegate != null) { |
| 646 while (!data.isEmpty() && (b = data.readByte()) != '\n') { | 689 while (!data.isEmpty() && (b = data.readByte()) != '\n') { |
| 647 bos.write(b); | 690 bos.write(b); |
| 648 } | 691 } |
| 649 Flags flags; | |
| 650 if (bos.size() == 0) { | 692 if (bos.size() == 0) { |
| 651 flags = Flags.RegularFile; | 693 flags = Flags.RegularFile; |
| 652 } else { | 694 } else { |
| 653 flags = Flags.parse(bos.toByteArray(), 0, bos.size()); | 695 flags = Flags.parse(bos.toByteArray(), 0, bos.size()); |
| 654 } | 696 } |
| 655 csetIndex2Flags.put(linkRevision, flags); | 697 |
| 698 } | |
| 699 if (delegate != null) { | |
| 700 assert flags != null; | |
| 701 assert fileRev != null; | |
| 702 delegate.begin(revisionNumber, Nodeid.fromBinary(nodeid, 0), linkRevision); | |
| 703 delegate.next(fileRev, filename, flags); | |
| 704 delegate.end(revisionNumber); | |
| 705 | |
| 706 } else { | |
| 707 if (csetIndex2FileRev != null) { | |
| 708 csetIndex2FileRev.put(linkRevision, fileRev); | |
| 709 } | |
| 710 if (csetIndex2Flags != null) { | |
| 711 csetIndex2Flags.put(linkRevision, flags); | |
| 712 } | |
| 656 } | 713 } |
| 657 break; | 714 break; |
| 658 } else { | 715 } else { |
| 659 data.skip(40); | 716 data.skip(40); |
| 660 } | 717 } |
