changeset 287:ed6b74a58c66

Use FileInfo abstraction with necessary subset of File functionality instead of File to facilitate other effective file system iterators
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 07 Sep 2011 09:33:27 +0200
parents 954763c82cc3
children b11f6a08f748
files src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java src/org/tmatesoft/hg/util/FileInfo.java src/org/tmatesoft/hg/util/FileIterator.java src/org/tmatesoft/hg/util/FileWalker.java src/org/tmatesoft/hg/util/RegularFileInfo.java
diffstat 5 files changed, 160 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java	Sat Sep 03 22:06:16 2011 +0200
+++ b/src/org/tmatesoft/hg/repo/HgWorkingCopyStatusCollector.java	Wed Sep 07 09:33:27 2011 +0200
@@ -21,10 +21,9 @@
 import static org.tmatesoft.hg.repo.HgRepository.*;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
+import java.nio.channels.ReadableByteChannel;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.NoSuchElementException;
@@ -41,11 +40,13 @@
 import org.tmatesoft.hg.internal.PathScope;
 import org.tmatesoft.hg.util.ByteChannel;
 import org.tmatesoft.hg.util.CancelledException;
+import org.tmatesoft.hg.util.FileInfo;
 import org.tmatesoft.hg.util.FileIterator;
 import org.tmatesoft.hg.util.FileWalker;
 import org.tmatesoft.hg.util.Path;
 import org.tmatesoft.hg.util.PathPool;
 import org.tmatesoft.hg.util.PathRewrite;
+import org.tmatesoft.hg.util.RegularFileInfo;
 
 /**
  *
@@ -162,7 +163,7 @@
 		while (repoWalker.hasNext()) {
 			repoWalker.next();
 			final Path fname = getPathPool().path(repoWalker.name());
-			File f = repoWalker.file();
+			FileInfo f = repoWalker.file();
 			if (!f.exists()) {
 				// file coming from iterator doesn't exist.
 				if (knownEntries.remove(fname)) {
@@ -193,7 +194,6 @@
 				}
 				continue;
 			}
-			assert f.isFile();
 			if (knownEntries.remove(fname)) {
 				// tracked file.
 				// modified, added, removed, clean
@@ -248,11 +248,11 @@
 	//********************************************
 
 	
-	private void checkLocalStatusAgainstFile(Path fname, File f, HgStatusInspector inspector) {
+	private void checkLocalStatusAgainstFile(Path fname, FileInfo f, HgStatusInspector inspector) {
 		HgDirstate.Record r;
 		if ((r = getDirstate().checkNormal(fname)) != null) {
 			// either clean or modified
-			final boolean timestampEqual = getFileModificationTime(f) == r.time, sizeEqual = r.size == f.length();
+			final boolean timestampEqual = f.lastModified() == r.time, sizeEqual = r.size == f.length();
 			if (timestampEqual && sizeEqual) {
 				inspector.clean(fname);
 			} else if (!sizeEqual && r.size >= 0) {
@@ -281,13 +281,8 @@
 		}
 	}
 	
-	// return mtime analog, directly comparable to dirstate's mtime.
-	private static int getFileModificationTime(File f) {
-		return (int) (f.lastModified() / 1000);
-	}
-	
 	// XXX refactor checkLocalStatus methods in more OO way
-	private void checkLocalStatusAgainstBaseRevision(Set<Path> baseRevNames, ManifestRevision collect, int baseRevision, Path fname, File f, HgStatusInspector inspector) {
+	private void checkLocalStatusAgainstBaseRevision(Set<Path> baseRevNames, ManifestRevision collect, int baseRevision, Path fname, FileInfo f, HgStatusInspector inspector) {
 		// fname is in the dirstate, either Normal, Added, Removed or Merged
 		Nodeid nid1 = collect.nodeid(fname);
 		HgManifest.Flags flags = collect.flags(fname);
@@ -325,7 +320,7 @@
 			if ((r = getDirstate().checkNormal(fname)) != null && nid1.equals(nidFromDirstate)) {
 				// regular file, was the same up to WC initialization. Check if was modified since, and, if not, report right away
 				// same code as in #checkLocalStatusAgainstFile
-				final boolean timestampEqual = getFileModificationTime(f) == r.time, sizeEqual = r.size == f.length();
+				final boolean timestampEqual = f.lastModified() == r.time, sizeEqual = r.size == f.length();
 				boolean handled = false;
 				if (timestampEqual && sizeEqual) {
 					inspector.clean(fname);
@@ -373,7 +368,7 @@
 		// The question is whether original Hg treats this case (same content, different parents and hence nodeids) as 'modified' or 'clean'
 	}
 
-	private boolean areTheSame(File f, HgDataFile dataFile, Nodeid revision) {
+	private boolean areTheSame(FileInfo f, HgDataFile dataFile, Nodeid revision) {
 		// XXX consider adding HgDataDile.compare(File/byte[]/whatever) operation to optimize comparison
 		ByteArrayChannel bac = new ByteArrayChannel();
 		boolean ioFailed = false;
@@ -390,12 +385,11 @@
 		return !ioFailed && areTheSame(f, bac.toArray(), dataFile.getPath());
 	}
 	
-	private boolean areTheSame(File f, final byte[] data, Path p) {
-		FileInputStream fis = null;
+	private boolean areTheSame(FileInfo f, final byte[] data, Path p) {
+		ReadableByteChannel is = null;
 		try {
 			try {
-				fis = new FileInputStream(f);
-				FileChannel fc = fis.getChannel();
+				is = f.newInputChannel();
 				ByteBuffer fb = ByteBuffer.allocate(min(1 + data.length * 2 /*to fit couple of lines appended; never zero*/, 8192));
 				class Check implements ByteChannel {
 					final boolean debug = false; // XXX may want to add global variable to allow clients to turn 
@@ -431,18 +425,18 @@
 				};
 				Check check = new Check(); 
 				FilterByteChannel filters = new FilterByteChannel(check, repo.getFiltersFromWorkingDirToRepo(p));
-				while (fc.read(fb) != -1 && check.sameSoFar()) {
+				while (is.read(fb) != -1 && check.sameSoFar()) {
 					fb.flip();
 					filters.write(fb);
 					fb.compact();
 				}
-				fis.close();
 				return check.ultimatelyTheSame();
 			} catch (IOException ex) {
-				if (fis != null) {
-					fis.close();
+				ex.printStackTrace(); // log warn
+			} finally {
+				if (is != null) {
+					is.close();
 				}
-				ex.printStackTrace(); // log warn
 			}
 		} catch (/*TODO typed*/Exception ex) {
 			ex.printStackTrace();
@@ -450,7 +444,7 @@
 		return false;
 	}
 
-	private static boolean todoCheckFlagsEqual(File f, HgManifest.Flags originalManifestFlags) {
+	private static boolean todoCheckFlagsEqual(FileInfo f, HgManifest.Flags originalManifestFlags) {
 		// FIXME implement
 		return true;
 	}
@@ -513,7 +507,7 @@
 		private final File dir;
 		private final Path[] paths;
 		private int index;
-		private File nextFile; // cache file() in case it's called more than once
+		private RegularFileInfo nextFile;
 
 		public FileListIterator(File startDir, Path... files) {
 			dir = startDir;
@@ -523,7 +517,7 @@
 
 		public void reset() {
 			index = -1;
-			nextFile = null;
+			nextFile = new RegularFileInfo();
 		}
 
 		public boolean hasNext() {
@@ -535,14 +529,14 @@
 			if (index == paths.length) {
 				throw new NoSuchElementException();
 			}
-			nextFile = new File(dir, paths[index].toString());
+			nextFile.init(new File(dir, paths[index].toString()));
 		}
 
 		public Path name() {
 			return paths[index];
 		}
 
-		public File file() {
+		public FileInfo file() {
 			return nextFile;
 		}
 
@@ -597,7 +591,7 @@
 			return walker.name();
 		}
 
-		public File file() {
+		public FileInfo file() {
 			return walker.file();
 		}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/util/FileInfo.java	Wed Sep 07 09:33:27 2011 +0200
@@ -0,0 +1,50 @@
+/*
+ * 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.nio.channels.ReadableByteChannel;
+
+/**
+ * Subset of File-related functionality to support other than {@link java.io.File}-based {@link FileIterator} implementations   
+ * 
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public interface FileInfo {
+	
+	/**
+	 * @return true if the filesystem object described by this instance exists, is a regular file and can be read
+	 */
+	boolean exists();
+	
+	/**
+	 * File last modification time, in seconds since Jan 1, 1970. E.g. <code> {@link java.io.File#lastModified()} / 1000 </code>
+	 * @return int value representing time, in seconds, when file was last modified.
+	 */
+	int lastModified();
+	
+	/**
+	 * @return file size
+	 */
+	long length();
+
+	/**
+	 * Access file contents. Caller is responsible to close the channel.
+	 * @return file reader object, never <code>null</code>
+	 */
+	ReadableByteChannel newInputChannel();
+}
--- a/src/org/tmatesoft/hg/util/FileIterator.java	Sat Sep 03 22:06:16 2011 +0200
+++ b/src/org/tmatesoft/hg/util/FileIterator.java	Wed Sep 07 09:33:27 2011 +0200
@@ -16,8 +16,6 @@
  */
 package org.tmatesoft.hg.util;
 
-import java.io.File;
-
 import org.tmatesoft.hg.internal.Experimental;
 
 /**
@@ -49,15 +47,15 @@
 	Path name();
 
 	/**
-	 * File object to retrieve actual state from. Not necessarily exist, if {@link FileIterator} is used to query status
-	 * of specific files.
-	 * @return filesystem element.
+	 * {@link FileInfo} object to retrieve actual file information. Caller shall not assume new instance for each {@link #next()} entry, 
+	 * implementors of this interface may reuse {@link FileInfo} instance if deemed suitable. 
+	 * @return file information holder.
 	 */
-	File file();
+	FileInfo file();
 
 	/**
 	 * When {@link FileIterator} represents only fraction of a repository, library might need to figure out if
-	 * specific file (path) belongs to that fraction or not. Paths (and {@link File Files} returned by this {@link FileIterator}
+	 * specific file (path) belongs to that fraction or not. Paths and files returned by this {@link FileIterator}
 	 * are always considered as representing the fraction, nonetheless, {@link FileIterator} shall return true for such names if 
 	 * asked.
 	 * @return <code>true</code> if this {@link FileIterator} is responsible for (interested in) specified repository-local path 
--- a/src/org/tmatesoft/hg/util/FileWalker.java	Sat Sep 03 22:06:16 2011 +0200
+++ b/src/org/tmatesoft/hg/util/FileWalker.java	Wed Sep 07 09:33:27 2011 +0200
@@ -32,7 +32,7 @@
 	private final LinkedList<File> dirQueue;
 	private final LinkedList<File> fileQueue;
 	private final Path.Matcher scope;
-	private File nextFile;
+	private RegularFileInfo nextFile;
 	private Path nextPath;
 
 	public FileWalker(File dir, Path.Source pathFactory) {
@@ -60,7 +60,7 @@
 		fileQueue.clear();
 		dirQueue.clear();
 		dirQueue.add(startDir);
-		nextFile = null;
+		nextFile = new RegularFileInfo();
 		nextPath = null;
 	}
 	
@@ -72,15 +72,16 @@
 		if (!fill()) {
 			throw new NoSuchElementException();
 		}
-		nextFile = fileQueue.removeFirst();
-		nextPath = pathHelper.path(nextFile.getPath());
+		File next = fileQueue.removeFirst();
+		nextFile.init(next);
+		nextPath = pathHelper.path(next.getPath());
 	}
 
 	public Path name() {
 		return nextPath;
 	}
 	
-	public File file() {
+	public FileInfo file() {
 		return nextFile;
 	}
 	
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/tmatesoft/hg/util/RegularFileInfo.java	Wed Sep 07 09:33:27 2011 +0200
@@ -0,0 +1,76 @@
+/*
+ * 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;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.ReadableByteChannel;
+
+/**
+ *
+ * @author Artem Tikhomirov
+ * @author TMate Software Ltd.
+ */
+public class RegularFileInfo implements FileInfo {
+	private File file;
+	
+	public RegularFileInfo() {
+	}
+	
+	public void init(File f) {
+		file = f;
+	}
+	
+	public boolean exists() {
+		return file.canRead() && file.isFile();
+	}
+
+	public int lastModified() {
+		return (int) (file.lastModified() / 1000);
+	}
+
+	public long length() {
+		return file.length();
+	}
+
+	public ReadableByteChannel newInputChannel() {
+		try {
+			return new FileInputStream(file).getChannel();
+		} catch (FileNotFoundException ex) {
+			ex.printStackTrace(); // FIXME log debug.
+			// shall not happen, provided this class is used correctly
+			return new ReadableByteChannel() {
+				
+				public boolean isOpen() {
+					return true;
+				}
+				
+				public void close() throws IOException {
+				}
+				
+				public int read(ByteBuffer dst) throws IOException {
+					// EOF right away
+					return -1;
+				}
+			};
+		}
+	}
+
+}