diff 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
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgBundle.java	Fri Jul 12 16:29:06 2013 +0200
+++ b/src/org/tmatesoft/hg/repo/HgBundle.java	Fri Jul 12 20:14:24 2013 +0200
@@ -17,8 +17,8 @@
 package org.tmatesoft.hg.repo;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.ConcurrentModificationException;
 
 import org.tmatesoft.hg.core.HgIOException;
@@ -27,18 +27,17 @@
 import org.tmatesoft.hg.internal.ByteArrayChannel;
 import org.tmatesoft.hg.internal.ByteArrayDataAccess;
 import org.tmatesoft.hg.internal.Callback;
+import org.tmatesoft.hg.internal.ChangesetParser;
 import org.tmatesoft.hg.internal.DataAccess;
+import org.tmatesoft.hg.internal.DataAccessInputStream;
 import org.tmatesoft.hg.internal.DataAccessProvider;
-import org.tmatesoft.hg.internal.DataSerializer;
 import org.tmatesoft.hg.internal.DigestHelper;
 import org.tmatesoft.hg.internal.EncodingHelper;
 import org.tmatesoft.hg.internal.Experimental;
-import org.tmatesoft.hg.internal.FileUtils;
 import org.tmatesoft.hg.internal.InflaterDataAccess;
 import org.tmatesoft.hg.internal.Internals;
 import org.tmatesoft.hg.internal.Lifecycle;
 import org.tmatesoft.hg.internal.Patch;
-import org.tmatesoft.hg.repo.HgChangelog.ChangesetParser;
 import org.tmatesoft.hg.repo.HgChangelog.RawChangeset;
 import org.tmatesoft.hg.util.Adaptable;
 import org.tmatesoft.hg.util.CancelledException;
@@ -54,9 +53,9 @@
 @Experimental(reason="API is not stable")
 public class HgBundle {
 
-	private final File bundleFile;
+	final File bundleFile;
 	private final DataAccessProvider accessProvider;
-	private final SessionContext ctx;
+	final SessionContext ctx;
 	private final EncodingHelper fnDecorer;
 	private Lifecycle.BasicCallback flowControl;
 
@@ -110,7 +109,7 @@
 	 * @param hgRepo repository that shall possess base revision for this bundle
 	 * @param inspector callback to get each changeset found 
 	 */
-	public void changes(final HgRepository hgRepo, final HgChangelog.Inspector inspector) throws HgRuntimeException {
+	public void changes(final HgRepository hgRepo, final HgChangelog.Inspector inspector) throws HgIOException, HgRuntimeException {
 		Inspector bundleInsp = new Inspector() {
 			DigestHelper dh = new DigestHelper();
 			boolean emptyChangelog = true;
@@ -121,7 +120,7 @@
 			public void changelogStart() {
 				emptyChangelog = true;
 				revisionIndex = 0;
-				csetBuilder = new ChangesetParser(hgRepo, true);
+				csetBuilder = new ChangesetParser(hgRepo, new HgChangelog.RawCsetFactory(true));
 			}
 
 			public void changelogEnd() {
@@ -153,7 +152,7 @@
 
 To recreate 30bd..e5, one have to take content of 9429..e0, not its p1 f1db..5e
  */
-			public boolean element(GroupElement ge) throws HgRuntimeException {
+			public boolean element(GroupElement ge) throws IOException, HgRuntimeException {
 				emptyChangelog = false;
 				HgChangelog changelog = hgRepo.getChangelog();
 				try {
@@ -172,20 +171,17 @@
 						}
 					}
 					//
-					byte[] csetContent = ge.apply(prevRevContent);
+					byte[] csetContent = ge.patch().apply(prevRevContent, -1);
 					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?
 					if (!ge.node().equalsTo(dh.asBinary())) {
 						throw new HgInvalidStateException(String.format("Integrity check failed on %s, node: %s", bundleFile, ge.node().shortNotation()));
 					}
-					ByteArrayDataAccess csetDataAccess = new ByteArrayDataAccess(csetContent);
-					RawChangeset cs = csetBuilder.parse(csetDataAccess);
+					RawChangeset cs = csetBuilder.parse(csetContent);
 					inspector.next(revisionIndex++, ge.node(), cs);
 					prevRevContent.done();
-					prevRevContent = csetDataAccess.reset();
+					prevRevContent = new ByteArrayDataAccess(csetContent);
 				} catch (CancelledException ex) {
 					return false;
-				} catch (IOException ex) {
-					throw new HgInvalidFileException("Invalid bundle file", ex, bundleFile); // TODO post-1.0 revisit exception handling
 				} catch (HgInvalidDataFormatException ex) {
 					throw new HgInvalidControlFileException("Invalid bundle file", ex, bundleFile);
 				}
@@ -217,11 +213,12 @@
 		void fileEnd(String name) throws HgRuntimeException;
 
 		/**
-		 * XXX desperately need exceptions here
 		 * @param element data element, instance might be reused, don't keep a reference to it or its raw data
 		 * @return <code>true</code> to continue
+		 * @throws IOException propagated exception from {@link GroupElement#data()}
+		 * @throws HgRuntimeException propagated exception (subclass thereof) to indicate issues with the library. <em>Runtime exception</em>
 		 */
-		boolean element(GroupElement element) throws HgRuntimeException;
+		boolean element(GroupElement element) throws IOException, HgRuntimeException;
 	}
 
 	/**
@@ -229,7 +226,7 @@
 	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
 	 * @throws IllegalArgumentException if inspector argument is null
 	 */
-	public void inspectChangelog(Inspector inspector) throws HgRuntimeException {
+	public void inspectChangelog(Inspector inspector) throws HgIOException, HgRuntimeException {
 		if (inspector == null) {
 			throw new IllegalArgumentException();
 		}
@@ -239,7 +236,7 @@
 			da = getDataStream();
 			internalInspectChangelog(da, inspector);
 		} catch (IOException ex) {
-			throw new HgInvalidFileException("Bundle.inspectChangelog failed", ex, bundleFile);
+			throw new HgIOException("Failed to inspect changelog in the bundle", ex, bundleFile);
 		} finally {
 			if (da != null) {
 				da.done();
@@ -253,7 +250,7 @@
 	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
 	 * @throws IllegalArgumentException if inspector argument is null
 	 */
-	public void inspectManifest(Inspector inspector) throws HgRuntimeException {
+	public void inspectManifest(Inspector inspector) throws HgIOException, HgRuntimeException {
 		if (inspector == null) {
 			throw new IllegalArgumentException();
 		}
@@ -267,7 +264,7 @@
 			skipGroup(da); // changelog
 			internalInspectManifest(da, inspector);
 		} catch (IOException ex) {
-			throw new HgInvalidFileException("Bundle.inspectManifest failed", ex, bundleFile);
+			throw new HgIOException("Failed to inspect manifest in the bundle", ex, bundleFile);
 		} finally {
 			if (da != null) {
 				da.done();
@@ -281,7 +278,7 @@
 	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
 	 * @throws IllegalArgumentException if inspector argument is null
 	 */
-	public void inspectFiles(Inspector inspector) throws HgRuntimeException {
+	public void inspectFiles(Inspector inspector) throws HgIOException, HgRuntimeException {
 		if (inspector == null) {
 			throw new IllegalArgumentException();
 		}
@@ -299,7 +296,7 @@
 			skipGroup(da); // manifest
 			internalInspectFiles(da, inspector);
 		} catch (IOException ex) {
-			throw new HgInvalidFileException("Bundle.inspectFiles failed", ex, bundleFile);
+			throw new HgIOException("Failed to inspect files in the bundle", ex, bundleFile);
 		} finally {
 			if (da != null) {
 				da.done();
@@ -313,7 +310,7 @@
 	 * @throws HgRuntimeException subclass thereof to indicate issues with the library. <em>Runtime exception</em>
 	 * @throws IllegalArgumentException if inspector argument is null
 	 */
-	public void inspectAll(Inspector inspector) throws HgRuntimeException {
+	public void inspectAll(Inspector inspector) throws HgIOException, HgRuntimeException {
 		if (inspector == null) {
 			throw new IllegalArgumentException();
 		}
@@ -331,7 +328,7 @@
 			}
 			internalInspectFiles(da, inspector);
 		} catch (IOException ex) {
-			throw new HgInvalidFileException("Bundle.inspectAll failed", ex, bundleFile);
+			throw new HgIOException("Failed to inspect bundle", ex, bundleFile);
 		} finally {
 			if (da != null) {
 				da.done();
@@ -453,13 +450,15 @@
 		}
 	}
 
-	@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")
-	public static class GroupElement {
+	/**
+	 * Describes single element (a.k.a. chunk) of the group, either changelog, manifest or a file. 
+	 */
+	public static final class GroupElement {
 		private final byte[] header; // byte[80] takes 120 bytes, 4 Nodeids - 192
 		private final DataAccess dataAccess;
+		private final Nodeid deltaBase;
 		private Patch patches;
-		private final Nodeid deltaBase;
-
+		
 		GroupElement(byte[] fourNodeids, Nodeid deltaBaseRev, DataAccess rawDataAccess) {
 			assert fourNodeids != null && fourNodeids.length == 80;
 			header = fourNodeids;
@@ -507,16 +506,15 @@
 			return deltaBase == null ? firstParent() : deltaBase;
 		}
 		
-		public byte[] rawDataByteArray() throws IOException { // XXX IOException or HgInvalidFileException?
-			return rawData().byteArray();
-		}
-		
-		public byte[] apply(byte[] baseContent) throws IOException {
-			return apply(new ByteArrayDataAccess(baseContent));
-		}
-
-		/*package-local*/ DataAccess rawData() {
-			return dataAccess;
+		/**
+		 * Read data of the group element. 
+		 * Note, {@link InputStream streams} obtained from several calls to this method
+		 * can't be read simultaneously.
+		 *  
+		 * @return stream to access content of this group element, never <code>null</code>
+		 */
+		public InputStream data() {
+			return new DataAccessInputStream(dataAccess);
 		}
 		
 		/*package-local*/ Patch patch() throws IOException {
@@ -528,10 +526,6 @@
 			return patches;
 		}
 
-		/*package-local*/ byte[] apply(DataAccess baseContent) throws IOException {
-			return patch().apply(baseContent, -1);
-		}
-		
 		public String toString() {
 			int patchCount;
 			try {
@@ -543,29 +537,4 @@
 			return String.format("%s %s %s %s; patches:%d\n", node().shortNotation(), firstParent().shortNotation(), secondParent().shortNotation(), cset().shortNotation(), patchCount);
 		}
 	}
-
-	@Experimental(reason="Work in progress, not an API")
-	public class BundleSerializer implements DataSerializer.DataSource {
-
-		public void serialize(DataSerializer out) throws HgIOException, HgRuntimeException {
-			FileInputStream fis = null;
-			try {
-				fis = new FileInputStream(HgBundle.this.bundleFile);
-				byte[] buffer = new byte[8*1024];
-				int r;
-				while ((r = fis.read(buffer, 0, buffer.length)) > 0) {
-					out.write(buffer, 0, r);
-				}
-				
-			} catch (IOException ex) {
-				throw new HgIOException("Failed to serialize bundle", HgBundle.this.bundleFile);
-			} finally {
-				new FileUtils(HgBundle.this.ctx.getLog(), this).closeQuietly(fis, HgBundle.this.bundleFile);
-			}
-		}
-
-		public int serializeLength() throws HgRuntimeException {
-			return Internals.ltoi(HgBundle.this.bundleFile.length());
-		}
-	}
 }