changeset 141:8248aae33f7d

Adopt FileIterator moving towards WCStatusCollector parameterizing. Improved path handling, move 'em around
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 17 Feb 2011 04:08:34 +0100
parents 1c1891ad1c73
children 37a34044e6bd
files src/org/tmatesoft/hg/internal/RelativePathRewrite.java src/org/tmatesoft/hg/repo/HgDirstate.java src/org/tmatesoft/hg/repo/HgIgnore.java src/org/tmatesoft/hg/repo/HgRepository.java src/org/tmatesoft/hg/repo/HgStatusCollector.java src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java src/org/tmatesoft/hg/util/FileIterator.java src/org/tmatesoft/hg/util/FileWalker.java src/org/tmatesoft/hg/util/Path.java src/org/tmatesoft/hg/util/PathPool.java src/org/tmatesoft/hg/util/PathRewrite.java
diffstat 11 files changed, 192 insertions(+), 47 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/internal/RelativePathRewrite.java	Thu Feb 17 04:08:34 2011 +0100
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2011 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.internal;
+
+import java.io.File;
+
+import org.tmatesoft.hg.util.PathRewrite;
+
+/**
+ *
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class RelativePathRewrite implements PathRewrite {
+	
+	private final String rootPath;
+
+	public RelativePathRewrite(File root) {
+		this(root.getPath());
+	}
+	
+	public RelativePathRewrite(String rootPath) {
+		this.rootPath = rootPath;
+	}
+
+	public String rewrite(String path) {
+		if (path != null && path.startsWith(rootPath)) {
+			if (path.length() == rootPath.length()) {
+				return "";
+			}
+			return path.substring(rootPath.length() + 1);
+		}
+		return path;
+	}
+}
--- a/src/org/tmatesoft/hg/repo/HgDirstate.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/repo/HgDirstate.java	Thu Feb 17 04:08:34 2011 +0100
@@ -25,6 +25,7 @@
 
 import org.tmatesoft.hg.internal.DataAccess;
 import org.tmatesoft.hg.internal.DataAccessProvider;
+import org.tmatesoft.hg.util.Path;
 
 
 /**
@@ -38,6 +39,7 @@
 
 	private final DataAccessProvider accessProvider;
 	private final File dirstateFile;
+	// deliberate String, not Path as it seems useless to keep Path here
 	private Map<String, Record> normal;
 	private Map<String, Record> added;
 	private Map<String, Record> removed;
@@ -126,18 +128,21 @@
 		return rv;
 	}
 	
-	/*package-local*/ Record checkNormal(String fname) {
-		return normal.get(fname);
+	/*package-local*/ Record checkNormal(Path fname) {
+		return normal.get(fname.toString());
 	}
 
-	/*package-local*/ Record checkAdded(String fname) {
-		return added.get(fname);
+	/*package-local*/ Record checkAdded(Path fname) {
+		return added.get(fname.toString());
+	}
+	/*package-local*/ Record checkRemoved(Path fname) {
+		return removed.get(fname.toString());
 	}
 	/*package-local*/ Record checkRemoved(String fname) {
 		return removed.get(fname);
 	}
-	/*package-local*/ Record checkMerged(String fname) {
-		return merged.get(fname);
+	/*package-local*/ Record checkMerged(Path fname) {
+		return merged.get(fname.toString());
 	}
 
 
--- a/src/org/tmatesoft/hg/repo/HgIgnore.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/repo/HgIgnore.java	Thu Feb 17 04:08:34 2011 +0100
@@ -25,7 +25,10 @@
 import java.util.List;
 import java.util.regex.Pattern;
 
+import org.tmatesoft.hg.util.Path;
+
 /**
+ * Handling of ignored paths according to .hgignore configuration
  * 
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
@@ -119,8 +122,8 @@
 		return sb.toString();
 	}
 
-	// TODO use Path and PathGlobMatcher
-	public boolean isIgnored(String path) {
+	// TODO use PathGlobMatcher
+	public boolean isIgnored(Path path) {
 		for (Pattern p : entries) {
 			if (p.matcher(path).find()) {
 				return true;
--- a/src/org/tmatesoft/hg/repo/HgRepository.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/repo/HgRepository.java	Thu Feb 17 04:08:34 2011 +0100
@@ -27,8 +27,10 @@
 import org.tmatesoft.hg.internal.ConfigFile;
 import org.tmatesoft.hg.internal.DataAccessProvider;
 import org.tmatesoft.hg.internal.Filter;
+import org.tmatesoft.hg.internal.RelativePathRewrite;
 import org.tmatesoft.hg.internal.RequiresFile;
 import org.tmatesoft.hg.internal.RevlogStream;
+import org.tmatesoft.hg.util.FileIterator;
 import org.tmatesoft.hg.util.FileWalker;
 import org.tmatesoft.hg.util.Path;
 import org.tmatesoft.hg.util.PathRewrite;
@@ -190,8 +192,13 @@
 	}
 
 	// FIXME not sure repository shall create walkers
-	/*package-local*/ FileWalker createWorkingDirWalker() {
-		return new FileWalker(repoDir.getParentFile());
+	/*package-local*/ FileIterator createWorkingDirWalker() {
+		File repoRoot = repoDir.getParentFile();
+		Path.Source pathSrc = new Path.SimpleSource(new PathRewrite.Composite(new RelativePathRewrite(repoRoot), getPathHelper()));
+		// Impl note: simple source is enough as files in the working dir are all unique
+		// even if they might get reused (i.e. after FileIterator#reset() and walking once again),
+		// path caching is better to be done in the code which knows that path are being reused 
+		return new FileWalker(repoRoot, pathSrc);
 	}
 
 	/**
--- a/src/org/tmatesoft/hg/repo/HgStatusCollector.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/repo/HgStatusCollector.java	Thu Feb 17 04:08:34 2011 +0100
@@ -161,11 +161,12 @@
 					inspector.modified(pp.path(fname));
 				}
 			} else {
-				String copyOrigin = getOriginIfCopy(repo, fname, r1Files, rev1);
+				Path copyTarget = pp.path(fname);
+				Path copyOrigin = getOriginIfCopy(repo, copyTarget, r1Files, rev1);
 				if (copyOrigin != null) {
-					inspector.copied(pp.path(copyOrigin), pp.path(fname));
+					inspector.copied(pp.path(copyOrigin) /*pipe through pool, just in case*/, copyTarget);
 				} else {
-					inspector.added(pp.path(fname));
+					inspector.added(copyTarget);
 				}
 			}
 		}
@@ -180,7 +181,7 @@
 		return rv;
 	}
 	
-	/*package-local*/static String getOriginIfCopy(HgRepository hgRepo, String fname, Collection<String> originals, int originalChangelogRevision) {
+	/*package-local*/static Path getOriginIfCopy(HgRepository hgRepo, Path fname, Collection<String> originals, int originalChangelogRevision) {
 		HgDataFile df = hgRepo.getFileNode(fname);
 		while (df.isCopy()) {
 			Path original = df.getCopySourceName();
@@ -192,7 +193,7 @@
 					// (both r1Files.contains is true and original was created earlier than rev1)
 					// without r1Files.contains changelogRevision <= rev1 won't suffice as the file
 					// might get removed somewhere in between (changelogRevision < R < rev1)
-					return original.toString();
+					return original;
 				}
 				break; // copy/rename done later
 			} 
--- a/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java	Thu Feb 17 04:08:34 2011 +0100
@@ -34,7 +34,7 @@
 import org.tmatesoft.hg.internal.FilterByteChannel;
 import org.tmatesoft.hg.repo.HgStatusCollector.ManifestRevisionInspector;
 import org.tmatesoft.hg.util.ByteChannel;
-import org.tmatesoft.hg.util.FileWalker;
+import org.tmatesoft.hg.util.FileIterator;
 import org.tmatesoft.hg.util.Path;
 import org.tmatesoft.hg.util.PathPool;
 import org.tmatesoft.hg.util.PathRewrite;
@@ -47,7 +47,7 @@
 public class HgWorkingCopyStatusCollector {
 
 	private final HgRepository repo;
-	private final FileWalker repoWalker;
+	private final FileIterator repoWalker;
 	private HgDirstate dirstate;
 	private HgStatusCollector baseRevisionCollector;
 	private PathPool pathPool;
@@ -56,7 +56,7 @@
 		this(hgRepo, hgRepo.createWorkingDirWalker());
 	}
 
-	HgWorkingCopyStatusCollector(HgRepository hgRepo, FileWalker hgRepoWalker) {
+	HgWorkingCopyStatusCollector(HgRepository hgRepo, FileIterator hgRepoWalker) {
 		this.repo = hgRepo;
 		this.repoWalker = hgRepoWalker;
 	}
@@ -122,11 +122,11 @@
 		final PathPool pp = getPathPool();
 		while (repoWalker.hasNext()) {
 			repoWalker.next();
-			String fname = repoWalker.name();
+			Path fname = repoWalker.name();
 			File f = repoWalker.file();
 			if (hgIgnore.isIgnored(fname)) {
 				inspector.ignored(pp.path(fname));
-			} else if (knownEntries.remove(fname)) {
+			} else if (knownEntries.remove(fname.toString())) {
 				// modified, added, removed, clean
 				if (collect != null) { // need to check against base revision, not FS file
 					checkLocalStatusAgainstBaseRevision(baseRevFiles, collect, baseRevision, fname, f, inspector);
@@ -167,7 +167,7 @@
 	//********************************************
 
 	
-	private void checkLocalStatusAgainstFile(String fname, File f, HgStatusInspector inspector) {
+	private void checkLocalStatusAgainstFile(Path fname, File f, HgStatusInspector inspector) {
 		HgDirstate.Record r;
 		if ((r = getDirstate().checkNormal(fname)) != null) {
 			// either clean or modified
@@ -194,17 +194,17 @@
 	}
 	
 	// XXX refactor checkLocalStatus methods in more OO way
-	private void checkLocalStatusAgainstBaseRevision(Set<String> baseRevNames, ManifestRevisionInspector collect, int baseRevision, String fname, File f, HgStatusInspector inspector) {
+	private void checkLocalStatusAgainstBaseRevision(Set<String> baseRevNames, ManifestRevisionInspector collect, int baseRevision, Path fname, File f, HgStatusInspector inspector) {
 		// fname is in the dirstate, either Normal, Added, Removed or Merged
-		Nodeid nid1 = collect.nodeid(fname);
-		String flags = collect.flags(fname);
+		Nodeid nid1 = collect.nodeid(fname.toString());
+		String flags = collect.flags(fname.toString());
 		HgDirstate.Record r;
 		if (nid1 == null) {
 			// normal: added?
 			// added: not known at the time of baseRevision, shall report
 			// merged: was not known, report as added?
 			if ((r = getDirstate().checkNormal(fname)) != null) {
-				String origin = HgStatusCollector.getOriginIfCopy(repo, fname, baseRevNames, baseRevision);
+				Path origin = HgStatusCollector.getOriginIfCopy(repo, fname, baseRevNames, baseRevision);
 				if (origin != null) {
 					inspector.copied(getPathPool().path(origin), getPathPool().path(fname));
 					return;
@@ -301,7 +301,7 @@
 		return false;
 	}
 
-	private static String todoGenerateFlags(String fname) {
+	private static String todoGenerateFlags(Path fname) {
 		// FIXME implement
 		return null;
 	}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/util/FileIterator.java	Thu Feb 17 04:08:34 2011 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011 TMate Software Ltd
+ *  
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * For information on how to redistribute this software under
+ * the terms of a license other than GNU General Public License
+ * contact TMate Software at support@hg4j.com
+ */
+package org.tmatesoft.hg.util;
+
+import java.io.File;
+
+/**
+ * Abstracts iteration over file system.
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public interface FileIterator {
+
+	/**
+	 * Brings iterator into initial state to facilitate new use.
+	 */
+	void reset();
+
+	/**
+	 * @return whether can shift to next element
+	 */
+	boolean hasNext();
+
+	/**
+	 * Shift to next element
+	 */
+	void next();
+
+	/**
+	 * @return repository-local path to the current element.
+	 */
+	Path name();
+
+	/**
+	 * @return filesystem element.
+	 */
+	File file();
+}
--- a/src/org/tmatesoft/hg/util/FileWalker.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/util/FileWalker.java	Thu Feb 17 04:08:34 2011 +0100
@@ -25,16 +25,18 @@
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
-public class FileWalker {
+public class FileWalker implements FileIterator {
 
 	private final File startDir;
+	private final Path.Source pathHelper;
 	private final LinkedList<File> dirQueue;
 	private final LinkedList<File> fileQueue;
 	private File nextFile;
-	private String nextPath;
+	private Path nextPath;
 
-	public FileWalker(File startDir) {
-		this.startDir = startDir;
+	public FileWalker(File dir, Path.Source pathFactory) {
+		startDir = dir;
+		pathHelper = pathFactory;
 		dirQueue = new LinkedList<File>();
 		fileQueue = new LinkedList<File>();
 		reset();
@@ -57,10 +59,10 @@
 			throw new NoSuchElementException();
 		}
 		nextFile = fileQueue.removeFirst();
-		nextPath = path(nextFile);
+		nextPath = pathHelper.path(nextFile.getPath());
 	}
 
-	public String name() {
+	public Path name() {
 		return nextPath;
 	}
 	
@@ -68,12 +70,6 @@
 		return nextFile;
 	}
 	
-	private String path(File f) {
-		// XXX LocalHgRepo#normalize
-		String p = f.getPath().substring(startDir.getPath().length() + 1);
-		return p.replace('\\', '/').replace("//", "/");
-	}
-
 	private File[] listFiles(File f) {
 		// in case we need to solve os-related file issues (mac with some encodings?)
 		return f.listFiles();
--- a/src/org/tmatesoft/hg/util/Path.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/util/Path.java	Thu Feb 17 04:08:34 2011 +0100
@@ -80,6 +80,31 @@
 	 * Path filter.
 	 */
 	public interface Matcher {
-		public boolean accept(Path path);
+		boolean accept(Path path);
+	}
+
+	/**
+	 * Factory for paths
+	 */
+	public interface Source {
+		Path path(String p);
+	}
+
+	/**
+	 * Straightforward {@link Source} implementation that creates new Path instance for each supplied string
+	 */
+	public static class SimpleSource implements Source {
+		private final PathRewrite normalizer;
+
+		public SimpleSource(PathRewrite pathRewrite) {
+			if (pathRewrite == null) {
+				throw new IllegalArgumentException();
+			}
+			normalizer = pathRewrite;
+		}
+
+		public Path path(String p) {
+			return Path.create(normalizer.rewrite(p));
+		}
 	}
 }
--- a/src/org/tmatesoft/hg/util/PathPool.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/util/PathPool.java	Thu Feb 17 04:08:34 2011 +0100
@@ -21,11 +21,12 @@
 
 
 /**
- *
+ * Produces path from strings and caches result for reuse
+ * 
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
-public class PathPool {
+public class PathPool implements Path.Source {
 	private final WeakHashMap<String, SoftReference<Path>> cache;
 	private final PathRewrite pathRewrite;
 	
@@ -36,13 +37,17 @@
 
 	public Path path(String p) {
 		p = pathRewrite.rewrite(p);
-		SoftReference<Path> sr = cache.get(p);
-		Path path = sr == null ? null : sr.get();
-		if (path == null) {
-			path = Path.create(p);
-			cache.put(p, new SoftReference<Path>(path));
+		return get(p, true);
+	}
+
+	// pipes path object through cache to reuse instance, if possible
+	public Path path(Path p) {
+		String s = pathRewrite.rewrite(p.toString());
+		Path cached = get(s, false);
+		if (cached == null) {
+			cache.put(s, new SoftReference<Path>(cached = p));
 		}
-		return path;
+		return cached;
 	}
 
 	// XXX what would be parent of an empty path?
--- a/src/org/tmatesoft/hg/util/PathRewrite.java	Wed Feb 16 22:33:04 2011 +0100
+++ b/src/org/tmatesoft/hg/util/PathRewrite.java	Thu Feb 17 04:08:34 2011 +0100
@@ -26,6 +26,7 @@
  */
 public interface PathRewrite {
 
+	// XXX think over CharSequence use instead of String
 	public String rewrite(String path);
 	
 	public static class Empty implements PathRewrite {