Mercurial > jhg
comparison src/org/tmatesoft/hg/repo/HgBundle.java @ 673:545b1d4cc11d
Refactor HgBundle.GroupElement (clear experimental mark), resolve few technical debt issues
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Fri, 12 Jul 2013 20:14:24 +0200 |
| parents | fba85bc1dfb8 |
| children |
comparison
equal
deleted
inserted
replaced
| 672:d2552e6a5af6 | 673:545b1d4cc11d |
|---|---|
| 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 java.io.File; | 19 import java.io.File; |
| 20 import java.io.FileInputStream; | |
| 21 import java.io.IOException; | 20 import java.io.IOException; |
| 21 import java.io.InputStream; | |
| 22 import java.util.ConcurrentModificationException; | 22 import java.util.ConcurrentModificationException; |
| 23 | 23 |
| 24 import org.tmatesoft.hg.core.HgIOException; | 24 import org.tmatesoft.hg.core.HgIOException; |
| 25 import org.tmatesoft.hg.core.Nodeid; | 25 import org.tmatesoft.hg.core.Nodeid; |
| 26 import org.tmatesoft.hg.core.SessionContext; | 26 import org.tmatesoft.hg.core.SessionContext; |
| 27 import org.tmatesoft.hg.internal.ByteArrayChannel; | 27 import org.tmatesoft.hg.internal.ByteArrayChannel; |
| 28 import org.tmatesoft.hg.internal.ByteArrayDataAccess; | 28 import org.tmatesoft.hg.internal.ByteArrayDataAccess; |
| 29 import org.tmatesoft.hg.internal.Callback; | 29 import org.tmatesoft.hg.internal.Callback; |
| 30 import org.tmatesoft.hg.internal.ChangesetParser; | |
| 30 import org.tmatesoft.hg.internal.DataAccess; | 31 import org.tmatesoft.hg.internal.DataAccess; |
| 32 import org.tmatesoft.hg.internal.DataAccessInputStream; | |
| 31 import org.tmatesoft.hg.internal.DataAccessProvider; | 33 import org.tmatesoft.hg.internal.DataAccessProvider; |
| 32 import org.tmatesoft.hg.internal.DataSerializer; | |
| 33 import org.tmatesoft.hg.internal.DigestHelper; | 34 import org.tmatesoft.hg.internal.DigestHelper; |
| 34 import org.tmatesoft.hg.internal.EncodingHelper; | 35 import org.tmatesoft.hg.internal.EncodingHelper; |
| 35 import org.tmatesoft.hg.internal.Experimental; | 36 import org.tmatesoft.hg.internal.Experimental; |
| 36 import org.tmatesoft.hg.internal.FileUtils; | |
| 37 import org.tmatesoft.hg.internal.InflaterDataAccess; | 37 import org.tmatesoft.hg.internal.InflaterDataAccess; |
| 38 import org.tmatesoft.hg.internal.Internals; | 38 import org.tmatesoft.hg.internal.Internals; |
| 39 import org.tmatesoft.hg.internal.Lifecycle; | 39 import org.tmatesoft.hg.internal.Lifecycle; |
| 40 import org.tmatesoft.hg.internal.Patch; | 40 import org.tmatesoft.hg.internal.Patch; |
| 41 import org.tmatesoft.hg.repo.HgChangelog.ChangesetParser; | |
| 42 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; | 41 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset; |
| 43 import org.tmatesoft.hg.util.Adaptable; | 42 import org.tmatesoft.hg.util.Adaptable; |
| 44 import org.tmatesoft.hg.util.CancelledException; | 43 import org.tmatesoft.hg.util.CancelledException; |
| 45 | 44 |
| 46 /** | 45 /** |
| 52 * @author TMate Software Ltd. | 51 * @author TMate Software Ltd. |
| 53 */ | 52 */ |
| 54 @Experimental(reason="API is not stable") | 53 @Experimental(reason="API is not stable") |
| 55 public class HgBundle { | 54 public class HgBundle { |
| 56 | 55 |
| 57 private final File bundleFile; | 56 final File bundleFile; |
| 58 private final DataAccessProvider accessProvider; | 57 private final DataAccessProvider accessProvider; |
| 59 private final SessionContext ctx; | 58 final SessionContext ctx; |
| 60 private final EncodingHelper fnDecorer; | 59 private final EncodingHelper fnDecorer; |
| 61 private Lifecycle.BasicCallback flowControl; | 60 private Lifecycle.BasicCallback flowControl; |
| 62 | 61 |
| 63 HgBundle(SessionContext sessionContext, DataAccessProvider dap, File bundle) { | 62 HgBundle(SessionContext sessionContext, DataAccessProvider dap, File bundle) { |
| 64 ctx = sessionContext; | 63 ctx = sessionContext; |
| 108 /** | 107 /** |
| 109 * Get changes recorded in the bundle that are missing from the supplied repository. | 108 * Get changes recorded in the bundle that are missing from the supplied repository. |
| 110 * @param hgRepo repository that shall possess base revision for this bundle | 109 * @param hgRepo repository that shall possess base revision for this bundle |
| 111 * @param inspector callback to get each changeset found | 110 * @param inspector callback to get each changeset found |
| 112 */ | 111 */ |
| 113 public void changes(final HgRepository hgRepo, final HgChangelog.Inspector inspector) throws HgRuntimeException { | 112 public void changes(final HgRepository hgRepo, final HgChangelog.Inspector inspector) throws HgIOException, HgRuntimeException { |
| 114 Inspector bundleInsp = new Inspector() { | 113 Inspector bundleInsp = new Inspector() { |
| 115 DigestHelper dh = new DigestHelper(); | 114 DigestHelper dh = new DigestHelper(); |
| 116 boolean emptyChangelog = true; | 115 boolean emptyChangelog = true; |
| 117 private DataAccess prevRevContent; | 116 private DataAccess prevRevContent; |
| 118 private int revisionIndex; | 117 private int revisionIndex; |
| 119 private ChangesetParser csetBuilder; | 118 private ChangesetParser csetBuilder; |
| 120 | 119 |
| 121 public void changelogStart() { | 120 public void changelogStart() { |
| 122 emptyChangelog = true; | 121 emptyChangelog = true; |
| 123 revisionIndex = 0; | 122 revisionIndex = 0; |
| 124 csetBuilder = new ChangesetParser(hgRepo, true); | 123 csetBuilder = new ChangesetParser(hgRepo, new HgChangelog.RawCsetFactory(true)); |
| 125 } | 124 } |
| 126 | 125 |
| 127 public void changelogEnd() { | 126 public void changelogEnd() { |
| 128 if (emptyChangelog) { | 127 if (emptyChangelog) { |
| 129 throw new IllegalStateException("No changelog group in the bundle"); // XXX perhaps, just be silent and/or log? | 128 throw new IllegalStateException("No changelog group in the bundle"); // XXX perhaps, just be silent and/or log? |
| 151 a6f39e595b2b54f56304470269a936ead77f5725 9429c7bd1920fab164a9d2b621d38d57bcb49ae0 30bd389788464287cee22ccff54c330a4b715de5 a6f39e595b2b54f56304470269a936ead77f5725; patches:3 | 150 a6f39e595b2b54f56304470269a936ead77f5725 9429c7bd1920fab164a9d2b621d38d57bcb49ae0 30bd389788464287cee22ccff54c330a4b715de5 a6f39e595b2b54f56304470269a936ead77f5725; patches:3 |
| 152 fd4f2c98995beb051070630c272a9be87bef617d 30bd389788464287cee22ccff54c330a4b715de5 0000000000000000000000000000000000000000 fd4f2c98995beb051070630c272a9be87bef617d; patches:3 | 151 fd4f2c98995beb051070630c272a9be87bef617d 30bd389788464287cee22ccff54c330a4b715de5 0000000000000000000000000000000000000000 fd4f2c98995beb051070630c272a9be87bef617d; patches:3 |
| 153 | 152 |
| 154 To recreate 30bd..e5, one have to take content of 9429..e0, not its p1 f1db..5e | 153 To recreate 30bd..e5, one have to take content of 9429..e0, not its p1 f1db..5e |
| 155 */ | 154 */ |
| 156 public boolean element(GroupElement ge) throws HgRuntimeException { | 155 public boolean element(GroupElement ge) throws IOException, HgRuntimeException { |
| 157 emptyChangelog = false; | 156 emptyChangelog = false; |
| 158 HgChangelog changelog = hgRepo.getChangelog(); | 157 HgChangelog changelog = hgRepo.getChangelog(); |
| 159 try { | 158 try { |
| 160 if (prevRevContent == null) { | 159 if (prevRevContent == null) { |
| 161 if (ge.firstParent().isNull() && ge.secondParent().isNull()) { | 160 if (ge.firstParent().isNull() && ge.secondParent().isNull()) { |
| 170 // extra byte[] (inside ByteArrayChannel) duplication just for the sake of subsequent ByteArrayDataChannel wrap. | 169 // extra byte[] (inside ByteArrayChannel) duplication just for the sake of subsequent ByteArrayDataChannel wrap. |
| 171 prevRevContent = new ByteArrayDataAccess(bac.toArray()); | 170 prevRevContent = new ByteArrayDataAccess(bac.toArray()); |
| 172 } | 171 } |
| 173 } | 172 } |
| 174 // | 173 // |
| 175 byte[] csetContent = ge.apply(prevRevContent); | 174 byte[] csetContent = ge.patch().apply(prevRevContent, -1); |
| 176 dh = dh.sha1(ge.firstParent(), ge.secondParent(), csetContent); // XXX ge may give me access to byte[] content of nodeid directly, perhaps, I don't need DH to be friend of Nodeid? | 175 dh = dh.sha1(ge.firstParent(), ge.secondParent(), csetContent); // XXX ge may give me access to byte[] content of nodeid directly, perhaps, I don't need DH to be friend of Nodeid? |
| 177 if (!ge.node().equalsTo(dh.asBinary())) { | 176 if (!ge.node().equalsTo(dh.asBinary())) { |
| 178 throw new HgInvalidStateException(String.format("Integrity check failed on %s, node: %s", bundleFile, ge.node().shortNotation())); | 177 throw new HgInvalidStateException(String.format("Integrity check failed on %s, node: %s", bundleFile, ge.node().shortNotation())); |
| 179 } | 178 } |
| 180 ByteArrayDataAccess csetDataAccess = new ByteArrayDataAccess(csetContent); | 179 RawChangeset cs = csetBuilder.parse(csetContent); |
| 181 RawChangeset cs = csetBuilder.parse(csetDataAccess); | |
| 182 inspector.next(revisionIndex++, ge.node(), cs); | 180 inspector.next(revisionIndex++, ge.node(), cs); |
| 183 prevRevContent.done(); | 181 prevRevContent.done(); |
| 184 prevRevContent = csetDataAccess.reset(); | 182 prevRevContent = new ByteArrayDataAccess(csetContent); |
| 185 } catch (CancelledException ex) { | 183 } catch (CancelledException ex) { |
| 186 return false; | 184 return false; |
| 187 } catch (IOException ex) { | |
| 188 throw new HgInvalidFileException("Invalid bundle file", ex, bundleFile); // TODO post-1.0 revisit exception handling | |
| 189 } catch (HgInvalidDataFormatException ex) { | 185 } catch (HgInvalidDataFormatException ex) { |
| 190 throw new HgInvalidControlFileException("Invalid bundle file", ex, bundleFile); | 186 throw new HgInvalidControlFileException("Invalid bundle file", ex, bundleFile); |
| 191 } | 187 } |
| 192 return true; | 188 return true; |
| 193 } | 189 } |
| 215 void fileStart(String name) throws HgRuntimeException; | 211 void fileStart(String name) throws HgRuntimeException; |
| 216 | 212 |
| 217 void fileEnd(String name) throws HgRuntimeException; | 213 void fileEnd(String name) throws HgRuntimeException; |
| 218 | 214 |
| 219 /** | 215 /** |
| 220 * XXX desperately need exceptions here | |
| 221 * @param element data element, instance might be reused, don't keep a reference to it or its raw data | 216 * @param element data element, instance might be reused, don't keep a reference to it or its raw data |
| 222 * @return <code>true</code> to continue | 217 * @return <code>true</code> to continue |
| 223 */ | 218 * @throws IOException propagated exception from {@link GroupElement#data()} |
| 224 boolean element(GroupElement element) throws HgRuntimeException; | 219 * @throws HgRuntimeException propagated exception (subclass thereof) to indicate issues with the library. <em>Runtime exception</em> |
| 220 */ | |
| 221 boolean element(GroupElement element) throws IOException, HgRuntimeException; | |
| 225 } | 222 } |
| 226 | 223 |
| 227 /** | 224 /** |
| 228 * @param inspector callback to visit changelog entries | 225 * @param inspector callback to visit changelog entries |
| 229 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> | 226 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
| 230 * @throws IllegalArgumentException if inspector argument is null | 227 * @throws IllegalArgumentException if inspector argument is null |
| 231 */ | 228 */ |
| 232 public void inspectChangelog(Inspector inspector) throws HgRuntimeException { | 229 public void inspectChangelog(Inspector inspector) throws HgIOException, HgRuntimeException { |
| 233 if (inspector == null) { | 230 if (inspector == null) { |
| 234 throw new IllegalArgumentException(); | 231 throw new IllegalArgumentException(); |
| 235 } | 232 } |
| 236 final Lifecycle lifecycle = lifecycleSetUp(inspector); | 233 final Lifecycle lifecycle = lifecycleSetUp(inspector); |
| 237 DataAccess da = null; | 234 DataAccess da = null; |
| 238 try { | 235 try { |
| 239 da = getDataStream(); | 236 da = getDataStream(); |
| 240 internalInspectChangelog(da, inspector); | 237 internalInspectChangelog(da, inspector); |
| 241 } catch (IOException ex) { | 238 } catch (IOException ex) { |
| 242 throw new HgInvalidFileException("Bundle.inspectChangelog failed", ex, bundleFile); | 239 throw new HgIOException("Failed to inspect changelog in the bundle", ex, bundleFile); |
| 243 } finally { | 240 } finally { |
| 244 if (da != null) { | 241 if (da != null) { |
| 245 da.done(); | 242 da.done(); |
| 246 } | 243 } |
| 247 lifecycleTearDown(lifecycle); | 244 lifecycleTearDown(lifecycle); |
| 251 /** | 248 /** |
| 252 * @param inspector callback to visit manifest entries | 249 * @param inspector callback to visit manifest entries |
| 253 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> | 250 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
| 254 * @throws IllegalArgumentException if inspector argument is null | 251 * @throws IllegalArgumentException if inspector argument is null |
| 255 */ | 252 */ |
| 256 public void inspectManifest(Inspector inspector) throws HgRuntimeException { | 253 public void inspectManifest(Inspector inspector) throws HgIOException, HgRuntimeException { |
| 257 if (inspector == null) { | 254 if (inspector == null) { |
| 258 throw new IllegalArgumentException(); | 255 throw new IllegalArgumentException(); |
| 259 } | 256 } |
| 260 final Lifecycle lifecycle = lifecycleSetUp(inspector); | 257 final Lifecycle lifecycle = lifecycleSetUp(inspector); |
| 261 DataAccess da = null; | 258 DataAccess da = null; |
| 265 return; | 262 return; |
| 266 } | 263 } |
| 267 skipGroup(da); // changelog | 264 skipGroup(da); // changelog |
| 268 internalInspectManifest(da, inspector); | 265 internalInspectManifest(da, inspector); |
| 269 } catch (IOException ex) { | 266 } catch (IOException ex) { |
| 270 throw new HgInvalidFileException("Bundle.inspectManifest failed", ex, bundleFile); | 267 throw new HgIOException("Failed to inspect manifest in the bundle", ex, bundleFile); |
| 271 } finally { | 268 } finally { |
| 272 if (da != null) { | 269 if (da != null) { |
| 273 da.done(); | 270 da.done(); |
| 274 } | 271 } |
| 275 lifecycleTearDown(lifecycle); | 272 lifecycleTearDown(lifecycle); |
| 279 /** | 276 /** |
| 280 * @param inspector callback to visit file entries | 277 * @param inspector callback to visit file entries |
| 281 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> | 278 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
| 282 * @throws IllegalArgumentException if inspector argument is null | 279 * @throws IllegalArgumentException if inspector argument is null |
| 283 */ | 280 */ |
| 284 public void inspectFiles(Inspector inspector) throws HgRuntimeException { | 281 public void inspectFiles(Inspector inspector) throws HgIOException, HgRuntimeException { |
| 285 if (inspector == null) { | 282 if (inspector == null) { |
| 286 throw new IllegalArgumentException(); | 283 throw new IllegalArgumentException(); |
| 287 } | 284 } |
| 288 final Lifecycle lifecycle = lifecycleSetUp(inspector); | 285 final Lifecycle lifecycle = lifecycleSetUp(inspector); |
| 289 DataAccess da = null; | 286 DataAccess da = null; |
| 297 return; | 294 return; |
| 298 } | 295 } |
| 299 skipGroup(da); // manifest | 296 skipGroup(da); // manifest |
| 300 internalInspectFiles(da, inspector); | 297 internalInspectFiles(da, inspector); |
| 301 } catch (IOException ex) { | 298 } catch (IOException ex) { |
| 302 throw new HgInvalidFileException("Bundle.inspectFiles failed", ex, bundleFile); | 299 throw new HgIOException("Failed to inspect files in the bundle", ex, bundleFile); |
| 303 } finally { | 300 } finally { |
| 304 if (da != null) { | 301 if (da != null) { |
| 305 da.done(); | 302 da.done(); |
| 306 } | 303 } |
| 307 lifecycleTearDown(lifecycle); | 304 lifecycleTearDown(lifecycle); |
| 311 /** | 308 /** |
| 312 * @param inspector visit complete bundle (changelog, manifest and file entries) | 309 * @param inspector visit complete bundle (changelog, manifest and file entries) |
| 313 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> | 310 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em> |
| 314 * @throws IllegalArgumentException if inspector argument is null | 311 * @throws IllegalArgumentException if inspector argument is null |
| 315 */ | 312 */ |
| 316 public void inspectAll(Inspector inspector) throws HgRuntimeException { | 313 public void inspectAll(Inspector inspector) throws HgIOException, HgRuntimeException { |
| 317 if (inspector == null) { | 314 if (inspector == null) { |
| 318 throw new IllegalArgumentException(); | 315 throw new IllegalArgumentException(); |
| 319 } | 316 } |
| 320 final Lifecycle lifecycle = lifecycleSetUp(inspector); | 317 final Lifecycle lifecycle = lifecycleSetUp(inspector); |
| 321 DataAccess da = null; | 318 DataAccess da = null; |
| 329 if (flowControl.isStopped()) { | 326 if (flowControl.isStopped()) { |
| 330 return; | 327 return; |
| 331 } | 328 } |
| 332 internalInspectFiles(da, inspector); | 329 internalInspectFiles(da, inspector); |
| 333 } catch (IOException ex) { | 330 } catch (IOException ex) { |
| 334 throw new HgInvalidFileException("Bundle.inspectAll failed", ex, bundleFile); | 331 throw new HgIOException("Failed to inspect bundle", ex, bundleFile); |
| 335 } finally { | 332 } finally { |
| 336 if (da != null) { | 333 if (da != null) { |
| 337 da.done(); | 334 da.done(); |
| 338 } | 335 } |
| 339 lifecycleTearDown(lifecycle); | 336 lifecycleTearDown(lifecycle); |
| 451 da.skip(len - 4); // sizeof(int) | 448 da.skip(len - 4); // sizeof(int) |
| 452 len = da.isEmpty() ? 0 : da.readInt(); | 449 len = da.isEmpty() ? 0 : da.readInt(); |
| 453 } | 450 } |
| 454 } | 451 } |
| 455 | 452 |
| 456 @Experimental(reason="Cumbersome API, rawData and apply with byte[] perhaps need replacement with ByteChannel/ByteBuffer, and better Exceptions. Perhaps, shall split into interface and impl") | 453 /** |
| 457 public static class GroupElement { | 454 * Describes single element (a.k.a. chunk) of the group, either changelog, manifest or a file. |
| 455 */ | |
| 456 public static final class GroupElement { | |
| 458 private final byte[] header; // byte[80] takes 120 bytes, 4 Nodeids - 192 | 457 private final byte[] header; // byte[80] takes 120 bytes, 4 Nodeids - 192 |
| 459 private final DataAccess dataAccess; | 458 private final DataAccess dataAccess; |
| 459 private final Nodeid deltaBase; | |
| 460 private Patch patches; | 460 private Patch patches; |
| 461 private final Nodeid deltaBase; | 461 |
| 462 | |
| 463 GroupElement(byte[] fourNodeids, Nodeid deltaBaseRev, DataAccess rawDataAccess) { | 462 GroupElement(byte[] fourNodeids, Nodeid deltaBaseRev, DataAccess rawDataAccess) { |
| 464 assert fourNodeids != null && fourNodeids.length == 80; | 463 assert fourNodeids != null && fourNodeids.length == 80; |
| 465 header = fourNodeids; | 464 header = fourNodeids; |
| 466 deltaBase = deltaBaseRev; | 465 deltaBase = deltaBaseRev; |
| 467 dataAccess = rawDataAccess; | 466 dataAccess = rawDataAccess; |
| 505 */ | 504 */ |
| 506 public Nodeid patchBase() { | 505 public Nodeid patchBase() { |
| 507 return deltaBase == null ? firstParent() : deltaBase; | 506 return deltaBase == null ? firstParent() : deltaBase; |
| 508 } | 507 } |
| 509 | 508 |
| 510 public byte[] rawDataByteArray() throws IOException { // XXX IOException or HgInvalidFileException? | 509 /** |
| 511 return rawData().byteArray(); | 510 * Read data of the group element. |
| 512 } | 511 * Note, {@link InputStream streams} obtained from several calls to this method |
| 513 | 512 * can't be read simultaneously. |
| 514 public byte[] apply(byte[] baseContent) throws IOException { | 513 * |
| 515 return apply(new ByteArrayDataAccess(baseContent)); | 514 * @return stream to access content of this group element, never <code>null</code> |
| 516 } | 515 */ |
| 517 | 516 public InputStream data() { |
| 518 /*package-local*/ DataAccess rawData() { | 517 return new DataAccessInputStream(dataAccess); |
| 519 return dataAccess; | |
| 520 } | 518 } |
| 521 | 519 |
| 522 /*package-local*/ Patch patch() throws IOException { | 520 /*package-local*/ Patch patch() throws IOException { |
| 523 if (patches == null) { | 521 if (patches == null) { |
| 524 dataAccess.reset(); | 522 dataAccess.reset(); |
| 526 patches.read(dataAccess); | 524 patches.read(dataAccess); |
| 527 } | 525 } |
| 528 return patches; | 526 return patches; |
| 529 } | 527 } |
| 530 | 528 |
| 531 /*package-local*/ byte[] apply(DataAccess baseContent) throws IOException { | |
| 532 return patch().apply(baseContent, -1); | |
| 533 } | |
| 534 | |
| 535 public String toString() { | 529 public String toString() { |
| 536 int patchCount; | 530 int patchCount; |
| 537 try { | 531 try { |
| 538 patchCount = patch().count(); | 532 patchCount = patch().count(); |
| 539 } catch (IOException ex) { | 533 } catch (IOException ex) { |
| 541 patchCount = -1; | 535 patchCount = -1; |
| 542 } | 536 } |
| 543 return String.format("%s %s %s %s; patches:%d\n", node().shortNotation(), firstParent().shortNotation(), secondParent().shortNotation(), cset().shortNotation(), patchCount); | 537 return String.format("%s %s %s %s; patches:%d\n", node().shortNotation(), firstParent().shortNotation(), secondParent().shortNotation(), cset().shortNotation(), patchCount); |
| 544 } | 538 } |
| 545 } | 539 } |
| 546 | |
| 547 @Experimental(reason="Work in progress, not an API") | |
| 548 public class BundleSerializer implements DataSerializer.DataSource { | |
| 549 | |
| 550 public void serialize(DataSerializer out) throws HgIOException, HgRuntimeException { | |
| 551 FileInputStream fis = null; | |
| 552 try { | |
| 553 fis = new FileInputStream(HgBundle.this.bundleFile); | |
| 554 byte[] buffer = new byte[8*1024]; | |
| 555 int r; | |
| 556 while ((r = fis.read(buffer, 0, buffer.length)) > 0) { | |
| 557 out.write(buffer, 0, r); | |
| 558 } | |
| 559 | |
| 560 } catch (IOException ex) { | |
| 561 throw new HgIOException("Failed to serialize bundle", HgBundle.this.bundleFile); | |
| 562 } finally { | |
| 563 new FileUtils(HgBundle.this.ctx.getLog(), this).closeQuietly(fis, HgBundle.this.bundleFile); | |
| 564 } | |
| 565 } | |
| 566 | |
| 567 public int serializeLength() throws HgRuntimeException { | |
| 568 return Internals.ltoi(HgBundle.this.bundleFile.length()); | |
| 569 } | |
| 570 } | |
| 571 } | 540 } |
