diff src/org/tmatesoft/hg/internal/FileAnnotation.java @ 555:e623aa2ca526

Annotate: RevisionDescriptor provides extra knowledge about inspected/annotated revision
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 22 Feb 2013 19:03:25 +0100
parents a5fd757d1b5d
children e55f17a7a195
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/FileAnnotation.java	Thu Feb 21 21:53:55 2013 +0100
+++ b/src/org/tmatesoft/hg/internal/FileAnnotation.java	Fri Feb 22 19:03:25 2013 +0100
@@ -18,179 +18,202 @@
 
 import java.util.LinkedList;
 
-import org.tmatesoft.hg.internal.AnnotateFacility.AddBlock;
-import org.tmatesoft.hg.internal.AnnotateFacility.BlockData;
-import org.tmatesoft.hg.internal.AnnotateFacility.ChangeBlock;
-import org.tmatesoft.hg.internal.AnnotateFacility.DeleteBlock;
-import org.tmatesoft.hg.internal.AnnotateFacility.EqualBlock;
-import org.tmatesoft.hg.internal.AnnotateFacility.LineInspector;
-
+import org.tmatesoft.hg.core.HgIterateDirection;
+import org.tmatesoft.hg.internal.AnnotateFacility.*;
+import org.tmatesoft.hg.repo.HgDataFile;
 
 /**
+ * Produce output like 'hg annotate' does
  * 
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
-public class FileAnnotation implements AnnotateFacility.BlockInspectorEx {
-		// blocks deleted in the target, as reported at the previous step
-		private LinkedList<DeleteBlock> deleted = new LinkedList<DeleteBlock>();
-		// blocks deleted in the origin, to become deletions in target at the next step
-		private LinkedList<DeleteBlock> newDeleted = new LinkedList<DeleteBlock>();
-		// keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from previous step
-		// XXX smth like IntSliceVector to access triples (or slices of any size, in fact)
-		// with easy indexing, e.g. #get(sliceIndex, indexWithinSlice)
-		// and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2)
-		private IntVector identical = new IntVector(20*3, 2*3);
-		// equal blocks of the current iteration, to be recalculated before next step
-		// to track line number (current target to ultimate target) mapping 
-		private IntVector newIdentical = new IntVector(20*3, 2*3);
-		
-		private boolean[] knownLines;
-		private final LineInspector delegate;
-		
-		public FileAnnotation(AnnotateFacility.LineInspector lineInspector) {
-			delegate = lineInspector;
-		}
-		
-		public void start(BlockData originContent, BlockData targetContent) {
-			if (knownLines == null) {
-				knownLines = new boolean[targetContent.elementCount()];
-			}
-		}
-
-//		private static void ppp(IntVector v) {
-//			for (int i = 0; i < v.size(); i+= 3) {
-//				int len = v.get(i+2);
-//				System.out.printf("[%d..%d) == [%d..%d);  ", v.get(i), v.get(i) + len, v.get(i+1), v.get(i+1) + len);
-//			}
-//			System.out.println();
-//		}
+public class FileAnnotation implements AnnotateFacility.BlockInspector, RevisionDescriptor.Recipient {
 
-		public void done() {
-			if (identical.size() > 0) {
-				// update line numbers of the intermediate target to point to ultimate target's line numbers
-				IntVector v = new IntVector(identical.size(), 2*3);
-				for (int i = 0; i < newIdentical.size(); i+= 3) {
-					int originLine = newIdentical.get(i);
-					int targetLine = newIdentical.get(i+1);
-					int length = newIdentical.get(i+2);
-					int startTargetLine = -1, startOriginLine = -1, c = 0;
-					for (int j = 0; j < length; j++) {
-						int lnInFinal = mapLineIndex(targetLine + j);
-						if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) {
-							// the line is not among "same" in ultimate origin
-							// or belongs to another/next "same" chunk 
-							if (startOriginLine == -1) {
-								continue;
-							}
-							v.add(startOriginLine);
-							v.add(startTargetLine);
-							v.add(c);
-							c = 0;
-							startOriginLine = startTargetLine = -1;
-							// fall-through to check if it's not complete miss but a next chunk
+	@Experimental(reason="The line-by-line inspector likely to become part of core/command API")
+	@Callback
+	public interface LineInspector {
+		/**
+		 * Not necessarily invoked sequentially by line numbers
+		 */
+		void line(int lineNumber, int changesetRevIndex, LineDescriptor ld);
+	}
+
+	public interface LineDescriptor {
+		int totalLines();
+	}
+
+	/**
+	 * Annotate file revision, line by line.
+	 */
+	public static void annotate(HgDataFile df, int changelogRevisionIndex, LineInspector insp) {
+		if (!df.exists()) {
+			return;
+		}
+		FileAnnotation fa = new FileAnnotation(insp);
+		AnnotateFacility af = new AnnotateFacility();
+		af.annotate(df, changelogRevisionIndex, fa, HgIterateDirection.NewToOld);
+	}
+
+	// blocks deleted in the target, as reported at the previous step
+	private LinkedList<DeleteBlock> deleted = new LinkedList<DeleteBlock>();
+	// blocks deleted in the origin, to become deletions in target at the next step
+	private LinkedList<DeleteBlock> newDeleted = new LinkedList<DeleteBlock>();
+	// keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from previous step
+	// XXX smth like IntSliceVector to access triples (or slices of any size, in fact)
+	// with easy indexing, e.g. #get(sliceIndex, indexWithinSlice)
+	// and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2)
+	private IntVector identical = new IntVector(20 * 3, 2 * 3);
+	// equal blocks of the current iteration, to be recalculated before next step
+	// to track line number (current target to ultimate target) mapping 
+	private IntVector newIdentical = new IntVector(20 * 3, 2 * 3);
+
+	private boolean[] knownLines;
+	private final LineInspector delegate;
+
+	public FileAnnotation(LineInspector lineInspector) {
+		delegate = lineInspector;
+	}
+
+	public void start(RevisionDescriptor rd) {
+		if (knownLines == null) {
+			knownLines = new boolean[rd.target().elementCount()];
+		}
+	}
+
+	//		private static void ppp(IntVector v) {
+	//			for (int i = 0; i < v.size(); i+= 3) {
+	//				int len = v.get(i+2);
+	//				System.out.printf("[%d..%d) == [%d..%d);  ", v.get(i), v.get(i) + len, v.get(i+1), v.get(i+1) + len);
+	//			}
+	//			System.out.println();
+	//		}
+
+	public void done(RevisionDescriptor rd) {
+		if (identical.size() > 0) {
+			// update line numbers of the intermediate target to point to ultimate target's line numbers
+			IntVector v = new IntVector(identical.size(), 2 * 3);
+			for (int i = 0; i < newIdentical.size(); i += 3) {
+				int originLine = newIdentical.get(i);
+				int targetLine = newIdentical.get(i + 1);
+				int length = newIdentical.get(i + 2);
+				int startTargetLine = -1, startOriginLine = -1, c = 0;
+				for (int j = 0; j < length; j++) {
+					int lnInFinal = mapLineIndex(targetLine + j);
+					if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) {
+						// the line is not among "same" in ultimate origin
+						// or belongs to another/next "same" chunk 
+						if (startOriginLine == -1) {
+							continue;
 						}
-						if (lnInFinal != -1) {
-							if (startOriginLine == -1) {
-								startOriginLine = originLine + j;
-								startTargetLine = lnInFinal;
-								c = 1;
-							} else {
-								assert lnInFinal == startTargetLine + c;
-								c++;
-							}
-						}
-					}
-					if (startOriginLine != -1) {
-						assert c > 0;
 						v.add(startOriginLine);
 						v.add(startTargetLine);
 						v.add(c);
+						c = 0;
+						startOriginLine = startTargetLine = -1;
+						// fall-through to check if it's not complete miss but a next chunk
+					}
+					if (lnInFinal != -1) {
+						if (startOriginLine == -1) {
+							startOriginLine = originLine + j;
+							startTargetLine = lnInFinal;
+							c = 1;
+						} else {
+							assert lnInFinal == startTargetLine + c;
+							c++;
+						}
 					}
 				}
-				newIdentical.clear();
-				identical = v;
-			} else {
-				IntVector li = newIdentical;
-				newIdentical = identical;
-				identical = li;
-			}
-			LinkedList<DeleteBlock> ld = newDeleted;
-			deleted.clear();
-			newDeleted = deleted;
-			deleted = ld;
-		}
-		
-		public void same(EqualBlock block) {
-			newIdentical.add(block.originStart());
-			newIdentical.add(block.targetStart());
-			newIdentical.add(block.length());
-		}
-
-		public void added(AddBlock block) {
-			for (int i = 0, ln = block.firstAddedLine(), x = block.totalAddedLines(); i < x; i++, ln++) {
-				int lnInFinal = mapLineIndex(ln);
-				if (lnInFinal != -1 && !knownLines[lnInFinal]) {
-					delegate.line(lnInFinal, block.targetChangesetIndex(), new LineDescriptor());
-					knownLines[lnInFinal] = true;
+				if (startOriginLine != -1) {
+					assert c > 0;
+					v.add(startOriginLine);
+					v.add(startTargetLine);
+					v.add(c);
 				}
 			}
+			newIdentical.clear();
+			identical = v;
+		} else {
+			IntVector li = newIdentical;
+			newIdentical = identical;
+			identical = li;
 		}
-		
-		public void changed(ChangeBlock block) {
-			deleted(block);
-			added(block);
+		LinkedList<DeleteBlock> ld = newDeleted;
+		deleted.clear();
+		newDeleted = deleted;
+		deleted = ld;
+	}
+
+	public void same(EqualBlock block) {
+		newIdentical.add(block.originStart());
+		newIdentical.add(block.targetStart());
+		newIdentical.add(block.length());
+	}
+
+	public void added(AddBlock block) {
+		for (int i = 0, ln = block.firstAddedLine(), x = block.totalAddedLines(); i < x; i++, ln++) {
+			int lnInFinal = mapLineIndex(ln);
+			if (lnInFinal != -1 && !knownLines[lnInFinal]) {
+				delegate.line(lnInFinal, block.targetChangesetIndex(), new LineDescriptorImpl());
+				knownLines[lnInFinal] = true;
+			}
+		}
+	}
+
+	public void changed(ChangeBlock block) {
+		deleted(block);
+		added(block);
+	}
+
+	public void deleted(DeleteBlock block) {
+		newDeleted.add(block);
+	}
+
+	// line - index in the target
+	private boolean isDeleted(int line) {
+		for (DeleteBlock b : deleted) {
+			if (b.firstRemovedLine() > line) {
+				break;
+			}
+			// line >= b.firstRemovedLine
+			if (b.firstRemovedLine() + b.totalRemovedLines() > line) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	// map target lines to the lines of the revision being annotated (the one that came first)
+	private int mapLineIndex(int ln) {
+		if (isDeleted(ln)) {
+			return -1;
+		}
+		if (identical.isEmpty()) {
+			return ln;
+		}
+		for (int i = 0; i < identical.size(); i += 3) {
+			final int originStart = identical.get(i);
+			if (originStart > ln) {
+				//					assert false;
+				return -1;
+			}
+			// ln >= b.originStart
+			final int length = identical.get(i + 2);
+			if (originStart + length > ln) {
+				int targetStart = identical.get(i + 1);
+				return targetStart + (ln - originStart);
+			}
+		}
+		//			assert false;
+		return -1;
+	}
+
+	private final class LineDescriptorImpl implements LineDescriptor {
+		LineDescriptorImpl() {
 		}
 
-		public void deleted(DeleteBlock block) {
-			newDeleted.add(block);
-		}
-		
-		// line - index in the target
-		private boolean isDeleted(int line) {
-			for (DeleteBlock b : deleted) {
-				if (b.firstRemovedLine() > line) {
-					break;
-				}
-				// line >= b.firstRemovedLine
-				if (b.firstRemovedLine() + b.totalRemovedLines() > line) {
-					return true;
-				}
-			}
-			return false;
+		public int totalLines() {
+			return FileAnnotation.this.knownLines.length;
 		}
-
-		// map target lines to the lines of the revision being annotated (the one that came first)
-		private int mapLineIndex(int ln) {
-			if (isDeleted(ln)) {
-				return -1;
-			}
-			if (identical.isEmpty()) {
-				return ln;
-			}
-			for (int i = 0; i < identical.size(); i += 3) {
-				final int originStart = identical.get(i);
-				if (originStart > ln) {
-//					assert false;
-					return -1;
-				}
-				// ln >= b.originStart
-				final int length = identical.get(i+2);
-				if (originStart + length > ln) {
-					int targetStart = identical.get(i+1);
-					return targetStart + (ln - originStart);
-				}
-			}
-//			assert false;
-			return -1;
-		}
-		
-		private final class LineDescriptor implements AnnotateFacility.LineDescriptor {
-			LineDescriptor() {
-			}
-			
-			public int totalLines() {
-				return FileAnnotation.this.knownLines.length;
-			}
-		}
-	}
\ No newline at end of file
+	}
+}
\ No newline at end of file