diff src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java @ 229:1ec6b327a6ac

Scope for status reworked: explicit files or a general matcher
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 31 May 2011 05:23:07 +0200
parents 26ad7827a62d
children 6e1373b54e9b
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java	Fri May 27 03:01:26 2011 +0200
+++ b/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java	Tue May 31 05:23:07 2011 +0200
@@ -19,14 +19,13 @@
 import static java.lang.Math.max;
 import static java.lang.Math.min;
 import static org.tmatesoft.hg.repo.HgRepository.*;
-import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION;
-import static org.tmatesoft.hg.repo.HgRepository.TIP;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.NoSuchElementException;
 import java.util.Set;
@@ -38,7 +37,7 @@
 import org.tmatesoft.hg.internal.ByteArrayChannel;
 import org.tmatesoft.hg.internal.Experimental;
 import org.tmatesoft.hg.internal.FilterByteChannel;
-import org.tmatesoft.hg.internal.RelativePathRewrite;
+import org.tmatesoft.hg.internal.PathScope;
 import org.tmatesoft.hg.repo.HgStatusCollector.ManifestRevisionInspector;
 import org.tmatesoft.hg.util.ByteChannel;
 import org.tmatesoft.hg.util.CancelledException;
@@ -62,10 +61,11 @@
 	private PathPool pathPool;
 
 	public HgWorkingCopyStatusCollector(HgRepository hgRepo) {
-		this(hgRepo, hgRepo.createWorkingDirWalker());
+		this(hgRepo, new HgInternals(hgRepo).createWorkingDirWalker(null));
 	}
 
-	HgWorkingCopyStatusCollector(HgRepository hgRepo, FileIterator hgRepoWalker) {
+	// FIXME document cons
+	public HgWorkingCopyStatusCollector(HgRepository hgRepo, FileIterator hgRepoWalker) {
 		repo = hgRepo;
 		repoWalker = hgRepoWalker;
 	}
@@ -136,7 +136,6 @@
 			repoWalker.next();
 			Path fname = pp.path(repoWalker.name());
 			File f = repoWalker.file();
-			assert f.isFile();
 			if (!f.exists()) {
 				// file coming from iterator doesn't exist.
 				if (knownEntries.remove(fname.toString())) {
@@ -167,6 +166,7 @@
 				}
 				continue;
 			}
+			assert f.isFile();
 			if (knownEntries.remove(fname.toString())) {
 				// tracked file.
 				// modified, added, removed, clean
@@ -399,11 +399,59 @@
 		return null;
 	}
 
-	@Experimental(reason="There's intention to support status query with multiple files/dirs, API might get changed")
-	public static HgWorkingCopyStatusCollector create(HgRepository hgRepo, Path file) {
-		FileIterator fi = file.isDirectory() ? new DirFileIterator(hgRepo, file) : new FileListIterator(hgRepo.getRepositoryRoot().getParentFile(), file);
+	/**
+	 * Configure status collector to consider only subset of a working copy tree. Tries to be as effective as possible, and to 
+	 * traverse only relevant part of working copy on the filesystem.
+	 * 
+	 * @param hgRepo repository
+	 * @param paths repository-relative files and/or directories. Directories are processed recursively. 
+	 * 
+	 * @return new instance of {@link HgWorkingCopyStatusCollector}, ready to {@link #walk(int, HgStatusInspector) walk} associated working copy 
+	 */
+	@Experimental(reason="Provisional API")
+	public static HgWorkingCopyStatusCollector create(HgRepository hgRepo, Path... paths) {
+		ArrayList<Path> f = new ArrayList<Path>(5);
+		ArrayList<Path> d = new ArrayList<Path>(5);
+		for (Path p : paths) {
+			if (p.isDirectory()) {
+				d.add(p);
+			} else {
+				f.add(p);
+			}
+		}
+//		final Path[] dirs = f.toArray(new Path[d.size()]);
+		if (d.isEmpty()) {
+			final Path[] files = f.toArray(new Path[f.size()]);
+			FileIterator fi = new FileListIterator(hgRepo.getRepositoryRoot().getParentFile(), files);
+			return new HgWorkingCopyStatusCollector(hgRepo, fi);
+		}
+		//
+		
+		//FileIterator fi = file.isDirectory() ? new DirFileIterator(hgRepo, file) : new FileListIterator(, file);
+		FileIterator fi = new HgInternals(hgRepo).createWorkingDirWalker(new PathScope(true, paths));
 		return new HgWorkingCopyStatusCollector(hgRepo, fi);
 	}
+	
+	/**
+	 * Configure collector object to calculate status for matching files only. 
+	 * This method may be less effective than explicit list of files as it iterates over whole repository 
+	 * (thus supplied matcher doesn't need to care if directories to files in question are also in scope, 
+	 * see {@link FileWalker#FileWalker(File, Path.Source, Path.Matcher)})
+	 *  
+	 * @return new instance of {@link HgWorkingCopyStatusCollector}, ready to {@link #walk(int, HgStatusInspector) walk} associated working copy
+	 */
+	@Experimental(reason="Provisional API. May add boolean strict argument for those who write smart matchers that can be used in FileWalker")
+	public static HgWorkingCopyStatusCollector create(HgRepository hgRepo, Path.Matcher scope) {
+		FileIterator w = new HgInternals(hgRepo).createWorkingDirWalker(null);
+		FileIterator wf = (scope == null || scope instanceof Path.Matcher.Any) ? w : new FileIteratorFilter(w, scope);
+		// the reason I need to iterate over full repo and apply filter is that I have no idea whatsoever about
+		// patterns in the scope. I.e. if scope lists a file (PathGlobMatcher("a/b/c.txt")), FileWalker won't get deep
+		// to the file unless matcher would also explicitly include "a/", "a/b/" in scope. Since I can't rely
+		// users would write robust matchers, and I don't see a decent way to enforce that (i.e. factory to produce
+		// correct matcher from Path is much like what PathScope does, and can be accessed directly with #create(repo, Path...)
+		// method above/
+		return new HgWorkingCopyStatusCollector(hgRepo, wf);
+	}
 
 	private static class FileListIterator implements FileIterator {
 		private final File dir;
@@ -452,15 +500,16 @@
 		}
 	}
 	
-	private static class DirFileIterator implements FileIterator {
-		private final Path dirOfInterest;
-		private final FileWalker walker;
+	private static class FileIteratorFilter implements FileIterator {
+		private final Path.Matcher filter;
+		private final FileIterator walker;
+		private boolean didNext = false;
 
-		public DirFileIterator(HgRepository hgRepo, Path directory) {
-			dirOfInterest = directory;
-			File dir = hgRepo.getRepositoryRoot().getParentFile();
-			Path.Source pathSrc = new Path.SimpleSource(new PathRewrite.Composite(new RelativePathRewrite(dir), hgRepo.getToRepoPathHelper()));
-			walker = new FileWalker(new File(dir, directory.toString()), pathSrc);
+		public FileIteratorFilter(FileIterator fileWalker, Path.Matcher filterMatcher) {
+			assert fileWalker != null;
+			assert filterMatcher != null;
+			filter = filterMatcher;
+			walker = fileWalker;
 		}
 
 		public void reset() {
@@ -468,11 +517,24 @@
 		}
 
 		public boolean hasNext() {
-			return walker.hasNext();
+			while (walker.hasNext()) {
+				walker.next();
+				if (filter.accept(walker.name())) {
+					didNext = true;
+					return true;
+				}
+			}
+			return false;
 		}
 
 		public void next() {
-			walker.next();
+			if (didNext) {
+				didNext = false;
+			} else {
+				if (!hasNext()) {
+					throw new NoSuchElementException();
+				}
+			}
 		}
 
 		public Path name() {
@@ -484,7 +546,7 @@
 		}
 
 		public boolean inScope(Path file) {
-			return file.toString().startsWith(dirOfInterest.toString());
+			return filter.accept(file);
 		}
 	}
 }