diff src/org/tmatesoft/hg/internal/PatchGenerator.java @ 544:7f5998a9619d

Refactor PatchGenerator to be generic and welcome sequence of any nature
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 15 Feb 2013 16:48:54 +0100
parents 1e95f48d9886
children 15b406c7cd9d
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/PatchGenerator.java	Fri Feb 15 15:52:03 2013 +0100
+++ b/src/org/tmatesoft/hg/internal/PatchGenerator.java	Fri Feb 15 16:48:54 2013 +0100
@@ -40,28 +40,27 @@
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
-public class PatchGenerator {
+public class PatchGenerator<T extends PatchGenerator.ChunkSequence<?>> {
 
-	private Map<ChunkSequence.ByteChain, IntVector> chunk2UseIndex;
-	private ChunkSequence seq1, seq2;
+	private Map<Chunk, IntVector> chunk2UseIndex;
+	private T seq1, seq2;
 
 	// get filled by #longestMatch, track start of common sequence in seq1 and seq2, respectively
 	private int matchStartS1, matchStartS2;
 
-	private MatchInspector matchInspector; 
+	private MatchInspector<T> matchInspector; 
 
-	public void init(byte[] data1, byte[] data2) {
-		seq1 = new ChunkSequence(data1);
-		seq1.splitByNewlines();
-		seq2 = new ChunkSequence(data2);
-		seq2.splitByNewlines();
-		prepare(seq2);
+	public void init(T s1, T s2) {
+		seq1 = s1;
+		seq2 = s2;
+		prepare(s2);
 	}
 
-	private void prepare(ChunkSequence s2) {
-		chunk2UseIndex = new HashMap<ChunkSequence.ByteChain, IntVector>();
+
+	private void prepare(T s2) {
+		chunk2UseIndex = new HashMap<Chunk, IntVector>();
 		for (int i = 0, len = s2.chunkCount(); i < len; i++) {
-			ChunkSequence.ByteChain bc = s2.chunk(i);
+			Chunk bc = s2.chunk(i);
 			IntVector loc = chunk2UseIndex.get(bc);
 			if (loc == null) {
 				chunk2UseIndex.put(bc, loc = new IntVector());
@@ -81,7 +80,7 @@
 //		}
 	}
 	
-	public void findMatchingBlocks(MatchInspector insp) {
+	public void findMatchingBlocks(MatchInspector<T> insp) {
 		insp.begin(seq1, seq2);
 		matchInspector = insp;
 		findMatchingBlocks(0, seq1.chunkCount(), 0, seq2.chunkCount());
@@ -96,7 +95,7 @@
 		int maxLength = 0;
 		IntMap<Integer> chunkIndex2MatchCount = new IntMap<Integer>(8);
 		for (int i = startS1; i < endS1; i++) {
-			ChunkSequence.ByteChain bc = seq1.chunk(i);
+			Chunk bc = seq1.chunk(i);
 			IntMap<Integer> newChunkIndex2MatchCount = new IntMap<Integer>(8);
 			IntVector occurencesInS2 = chunk2UseIndex.get(bc);
 			if (occurencesInS2 == null) {
@@ -140,14 +139,14 @@
 		}
 	}
 	
-	interface MatchInspector {
-		void begin(ChunkSequence s1, ChunkSequence s2);
+	interface MatchInspector<T extends ChunkSequence<?>> {
+		void begin(T s1, T s2);
 		void match(int startSeq1, int startSeq2, int matchLength);
 		void end();
 	}
 	
-	static class MatchDumpInspector implements MatchInspector {
-		public void begin(ChunkSequence s1, ChunkSequence s2) {
+	static class MatchDumpInspector<T extends ChunkSequence<?>> implements MatchInspector<T> {
+		public void begin(T s1, T s2) {
 		}
 
 		public void match(int startSeq1, int startSeq2, int matchLength) {
@@ -158,12 +157,11 @@
 		}
 	}
 	
-	static class DeltaInspector implements MatchInspector {
+	static class DeltaInspector<T extends ChunkSequence<?>> implements MatchInspector<T> {
 		protected int changeStartS1, changeStartS2;
-		protected ChunkSequence seq1, seq2;
-		
+		protected T seq1, seq2;
 
-		public void begin(ChunkSequence s1, ChunkSequence s2) {
+		public void begin(T s1, T s2) {
 			seq1 = s1;
 			seq2 = s2;
 			changeStartS1 = changeStartS2 = 0;
@@ -223,7 +221,7 @@
 		}
 	}
 	
-	static class DeltaDumpInspector extends DeltaInspector {
+	static class DeltaDumpInspector<T extends ChunkSequence<?>> extends DeltaInspector<T> {
 
 		@Override
 		protected void changed(int s1From, int s1To, int s2From, int s2To) {
@@ -242,44 +240,11 @@
 		
 	}
 	
-	static class PatchFillInspector extends DeltaInspector {
-		private final Patch deltaCollector;
-		
-		PatchFillInspector(Patch p) {
-			assert p != null;
-			deltaCollector = p;
-		}
-		
-		@Override
-		protected void changed(int s1From, int s1To, int s2From, int s2To) {
-			int from = seq1.chunk(s1From).getOffset();
-			int to = seq1.chunk(s1To).getOffset();
-			byte[] data = seq2.data(s2From, s2To);
-			deltaCollector.add(from, to, data);
-		}
-		
-		@Override
-		protected void deleted(int s2DeletionPoint, int s1From, int s1To) {
-			int from = seq1.chunk(s1From).getOffset();
-			int to = seq1.chunk(s1To).getOffset();
-			deltaCollector.add(from, to, new byte[0]);
-		}
-		
-		@Override
-		protected void added(int s1InsertPoint, int s2From, int s2To) {
-			int insPoint = seq1.chunk(s1InsertPoint).getOffset();
-			byte[] data = seq2.data(s2From, s2To);
-			deltaCollector.add(insPoint, insPoint, data);
-		}
-	}
-	
-	
-	
 	public static void main(String[] args) throws Exception {
-		PatchGenerator pg1 = new PatchGenerator();
-		pg1.init("hello".getBytes(), "hello\nworld".getBytes());
-		pg1.findMatchingBlocks(new MatchDumpInspector());
-		pg1.findMatchingBlocks(new DeltaDumpInspector());
+		PatchGenerator<LineSequence> pg1 = new PatchGenerator<LineSequence>();
+		pg1.init(LineSequence.newlines("hello".getBytes()), LineSequence.newlines("hello\nworld".getBytes()));
+		pg1.findMatchingBlocks(new MatchDumpInspector<LineSequence>());
+		pg1.findMatchingBlocks(new DeltaDumpInspector<LineSequence>());
 		if (Boolean.FALSE.booleanValue()) {
 			return;
 		}
@@ -290,35 +255,45 @@
 		df.content(81, bac2 = new ByteArrayChannel());
 //		String s1 = "line 1\nline 2\r\nline 3\n\nline 1\nline 2";
 //		String s2 = "abc\ncdef\r\nline 2\r\nline 3\nline 2";
-		PatchGenerator pg = new PatchGenerator();
-		pg.init(bac1.toArray(), bac2.toArray());
+		PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>();
+		byte[] data1 = bac1.toArray();
+		byte[] data2 = bac2.toArray();
+		pg.init(new LineSequence(data1).splitByNewlines(), new LineSequence(data2).splitByNewlines());
 		System.out.println("Matches:");
-		pg.findMatchingBlocks(new MatchDumpInspector());
+		pg.findMatchingBlocks(new MatchDumpInspector<LineSequence>());
 		System.out.println("Deltas:");
-		pg.findMatchingBlocks(new DeltaDumpInspector());
+		pg.findMatchingBlocks(new DeltaDumpInspector<LineSequence>());
+	}
+
+	/**
+	 * Unsure if this marker interface worth presence
+	 */
+	public interface Chunk {
 	}
 	
-	public Patch delta(byte[] prev, byte[] content) {
-		Patch rv = new Patch();
-		init(prev, content);
-		findMatchingBlocks(new PatchFillInspector(rv));
-		return rv;
+	/**
+	 * Generic sequence of chunk, where chunk is anything comparable to another chunk, e.g. a string or a single char
+	 * Sequence diff algorithm above doesn't care about sequence nature.
+	 */
+	public interface ChunkSequence<T extends Chunk> {
+		public T chunk(int index);
+		public int chunkCount();
 	}
 	
-	/*
-	 * TODO shall be parameterized (template?) and refacctored to facilitate matching non lines only
-	 * (sequence diff algorithm above doesn't care about sequence nature)
-	 */
-	static final class ChunkSequence {
+	static final class LineSequence implements ChunkSequence<LineSequence.ByteChain> {
 		
 		private final byte[] input;
 		private ArrayList<ByteChain> lines;
 
-		public ChunkSequence(byte[] data) {
+		public LineSequence(byte[] data) {
 			input = data;
 		}
 		
-		public void splitByNewlines() {
+		public static LineSequence newlines(byte[] array) {
+			return new LineSequence(array).splitByNewlines();
+		}
+
+		public LineSequence splitByNewlines() {
 			lines = new ArrayList<ByteChain>();
 			int lastStart = 0;
 			for (int i = 0; i < input.length; i++) {
@@ -338,6 +313,7 @@
 			}
 			// empty chunk to keep offset of input end
 			lines.add(new ByteChain(input.length, input.length));
+			return this;
 		}
 		
 		public ByteChain chunk(int index) {
@@ -359,7 +335,7 @@
 		}
 
 		
-		final class ByteChain {
+		final class ByteChain implements Chunk {
 			private final int start, end;
 			private final int hash;
 			
@@ -396,7 +372,7 @@
 			
 			private boolean match(byte[] oi, int from) {
 				for (int i = start, j = from; i < end; i++, j++) {
-					if (ChunkSequence.this.input[i] != oi[j]) {
+					if (LineSequence.this.input[i] != oi[j]) {
 						return false;
 					}
 				}