changeset 270:b21915a3fc24

Servlet version 1.21b3 - searching in directories got faster (real binarySearch now!) - cached file lists get disposed - some code cleaning (Map types instead of HashMap)
author robcast
date Tue, 12 Oct 2004 16:06:43 +0200
parents 6e6bf5aa7ad2
children d3abaf38fb5f
files servlet/src/digilib/io/Directory.java servlet/src/digilib/io/DocuDirCache.java servlet/src/digilib/io/DocuDirectory.java
diffstat 3 files changed, 211 insertions(+), 158 deletions(-) [+]
line wrap: on
line diff
--- a/servlet/src/digilib/io/Directory.java	Tue Oct 12 16:06:43 2004 +0200
+++ b/servlet/src/digilib/io/Directory.java	Tue Oct 12 16:06:43 2004 +0200
@@ -125,10 +125,15 @@
 	public String[] getFilenames() {
 		return list;
 	}
+	
 	/**
 	 * @param filenames The filenames to set.
 	 */
 	public void setFilenames(String[] filenames) {
 		this.list = filenames;
 	}
+	
+	public void clearFilenames() {
+		this.list = null;
+	}
 }
--- a/servlet/src/digilib/io/DocuDirCache.java	Tue Oct 12 16:06:43 2004 +0200
+++ b/servlet/src/digilib/io/DocuDirCache.java	Tue Oct 12 16:06:43 2004 +0200
@@ -27,6 +27,7 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.log4j.Logger;
 
@@ -38,21 +39,23 @@
 public class DocuDirCache {
 
 	/** general logger for this class */
-	protected Logger logger = Logger.getLogger(this.getClass());
+	Logger logger = Logger.getLogger(this.getClass());
 	/** HashMap of directories */
-	protected HashMap map = null;
+	Map map = null;
 	/** names of base directories */
-	private String[] baseDirNames = null;
+	String[] baseDirNames = null;
 	/** array of allowed file classes (image/text) */
 	private int[] fileClasses = null;
 	/** number of files in the whole cache (approximate) */
-	private long numFiles = 0;
+	long numFiles = 0;
 	/** number of cache hits */
-	private long hits = 0;
+	long hits = 0;
 	/** number of cache misses */
-	private long misses = 0;
+	long misses = 0;
 	/** use safe (but slow) indexing */
 	boolean safeDirIndex = false;
+	/** the root directory element */
+	public static Directory ROOT = null;
 
 	/**
 	 * Constructor with array of base directory names and file classes.
@@ -111,8 +114,8 @@
 	 */
 	public void putDir(DocuDirectory newDir) {
 		put(newDir);
-		String parent = newDir.getParentDirName();
-		if (parent != null) {
+		String parent = FileOps.parent(newDir.getDirName());
+		if (parent != "") {
 			// check the parent in the cache
 			DocuDirectory pd = (DocuDirectory) map.get(parent);
 			if (pd == null) {
@@ -146,7 +149,7 @@
 					l.add(dd);
 				}
 			} else {
-				if (dd.getParentDirName().equals(dirname)) {
+				if (FileOps.parent(dd.getDirName()).equals(dirname)) {
 					l.add(dd);
 				}
 			}
--- a/servlet/src/digilib/io/DocuDirectory.java	Tue Oct 12 16:06:43 2004 +0200
+++ b/servlet/src/digilib/io/DocuDirectory.java	Tue Oct 12 16:06:43 2004 +0200
@@ -1,20 +1,20 @@
 /* DocuDirectory -- Directory of DocuFilesets.
 
-  Digital Image Library servlet components
+ Digital Image Library servlet components
 
-  Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de)
+ Copyright (C) 2003 Robert Casties (robcast@mail.berlios.de)
 
-  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;  either version 2 of the  License, or (at your
-  option) any later version.
-   
-  Please read license.txt for the full details. A copy of the GPL
-  may be found at http://www.gnu.org/copyleft/lgpl.html
+ 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;  either version 2 of the  License, or (at your
+ option) any later version.
+ 
+ Please read license.txt for the full details. A copy of the GPL
+ may be found at http://www.gnu.org/copyleft/lgpl.html
 
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
  * Created on 25.02.2003
  */
@@ -25,7 +25,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashMap;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -37,34 +37,45 @@
  */
 public class DocuDirectory extends Directory {
 
-	// list of files (DocuDirent)
+	/** list of files (DocuDirent) */
 	private ArrayList[] list = null;
-	// directory object is valid (exists on disk)
+
+	/** directory object is valid (exists on disk) */
 	private boolean isValid = false;
-	// reference of the parent DocuDirCache
+
+	/** reference of the parent DocuDirCache */
 	private DocuDirCache cache = null;
-	// directory name (digilib canonical form)
+
+	/** directory name (digilib canonical form) */
 	private String dirName = null;
-	// directory metadata
-	private HashMap dirMeta = null;
-	// state of metadata is valid
+
+	/** directory metadata */
+	private Map dirMeta = null;
+
+	/** state of metadata is valid */
 	private boolean metaChecked = false;
-	// unresolved file metadata
-	private HashMap unresolvedFileMeta = null;
-	// time of last access of this object (not the filesystem)
+
+	/** unresolved file metadata */
+	private Map unresolvedFileMeta = null;
+
+	/** time of last access of this object (not the filesystem) */
 	private long objectATime = 0;
-	// time the file system directory was last modified
+
+	/** time directory was last modified on the file system */
 	private long dirMTime = 0;
 
-	/** Constructor with digilib directory path and a parent DocuDirCache.
+	/**
+	 * Constructor with digilib directory path and a parent DocuDirCache.
 	 * 
-	 * Directory names at the given path are appended to the base directories 
-	 * from the cache. The directory is checked on disk and isValid is set. 
+	 * Directory names at the given path are appended to the base directories
+	 * from the cache. The directory is checked on disk and isValid is set.
 	 * 
 	 * @see readDir
-	 *  
-	 * @param path digilib directory path name
-	 * @param cache parent DocuDirCache
+	 * 
+	 * @param path
+	 *            digilib directory path name
+	 * @param cache
+	 *            parent DocuDirCache
 	 */
 	public DocuDirectory(String path, DocuDirCache cache) {
 		this.dirName = path;
@@ -73,8 +84,9 @@
 		checkDir();
 	}
 
-	/** Sets and checks the dir object.
-	 * 
+	/**
+	 * Sets and checks the dir object.
+	 *  
 	 */
 	protected void initDir() {
 		String baseDirName = cache.getBaseDirNames()[0];
@@ -86,35 +98,26 @@
 		dir = new File(baseDirName, dirName);
 	}
 
-	/** The digilib name of the parent directory.
-	 * 
-	 * Returns null if there is no parent.
+	/**
+	 * number of DocuFiles in this directory.
+	 *  
 	 */
-	public String getParentDirName() {
-		String s = null;
-		int lastidx = dirName.lastIndexOf("/");
-		if (lastidx > 0) {
-			s = dirName.substring(0, lastidx);
-		}
-		return s;
+	public int size() {
+		return ((list != null) && (list[0] != null)) ? list[0].size() : 0;
 	}
 
-	/** number of DocuFiles in this directory.
+	/**
+	 * number of files of this class in this directory.
 	 * 
+	 * @param fc
+	 *            fileClass
 	 */
-	public int size() {
-		return ((list != null)&&(list[0] != null)) ? list[0].size() : 0;
+	public int size(int fc) {
+		return ((list != null) && (list[fc] != null)) ? list[fc].size() : 0;
 	}
 
-	/** number of files of this class in this directory.
-	 * 
-	 * @param fc fileClass
-	 */
-	public int size(int fc) {
-		return ((list != null)&&(list[fc] != null)) ? list[fc].size() : 0;
-	}
-
-	/** Returns the ImageFile at the index.
+	/**
+	 * Returns the ImageFile at the index.
 	 * 
 	 * @param index
 	 * @return
@@ -126,10 +129,12 @@
 		return (ImageFileset) list[0].get(index);
 	}
 
-	/** Returns the file of the class at the index.
+	/**
+	 * Returns the file of the class at the index.
 	 * 
 	 * @param index
-	 * @param fc fileClass
+	 * @param fc
+	 *            fileClass
 	 * @return
 	 */
 	public DocuDirent get(int index, int fc) {
@@ -139,7 +144,8 @@
 		return (DocuDirent) list[fc].get(index);
 	}
 
-	/** Checks if the directory exists on the filesystem.
+	/**
+	 * Checks if the directory exists on the filesystem.
 	 * 
 	 * Sets isValid.
 	 * 
@@ -153,42 +159,26 @@
 		return isValid;
 	}
 
-	/** Read the filesystem directory and fill this object.
+	/**
+	 * Read the filesystem directory and fill this object.
 	 * 
 	 * Clears the List and (re)reads all files.
 	 * 
 	 * @return boolean the directory exists
 	 */
 	public boolean readDir() {
-		// list of base dirs from the parent cache
-		String[] baseDirNames = cache.getBaseDirNames();
-		// first file extension to try for scaled directories
-		String scalext = null;
-		// number of base dirs
-		int nb = baseDirNames.length;
-		// array of base dirs
-		Directory[] dirs = new Directory[nb];
 		// check directory first
 		checkDir();
 		if (!isValid) {
 			return false;
 		}
-		// first entry is this directory
-		dirs[0] = this;
-		// fill array with the remaining directories
-		for (int j = 1; j < nb; j++) {
-			File d = new File(baseDirNames[j], dirName);
-			if (d.isDirectory()) {
-				dirs[j] = new Directory(d);
-				dirs[j].readDir();
-			}
-		}
-
+		// first file extension to try for scaled directories
+		String scalext = null;
 		// read all filenames
-		logger.debug("reading directory "+dir.getPath());
+		logger.debug("reading directory " + dir.getPath());
 		/*
-		 * using ReadableFileFilter is safer (we won't get directories
-		 * with file extensions) but slower.
+		 * using ReadableFileFilter is safer (we won't get directories with file
+		 * extensions) but slower.
 		 */
 		File[] allFiles = null;
 		if (cache.safeDirIndex) {
@@ -201,36 +191,67 @@
 			// not a directory
 			return false;
 		}
+		// list of base dirs from the parent cache
+		String[] baseDirNames = cache.getBaseDirNames();
+		// number of base dirs
+		int nb = baseDirNames.length;
+		// array of base dirs
+		Directory[] dirs = new Directory[nb];
+		// first entry is this directory
+		dirs[0] = this;
+		// fill array with the remaining directories
+		for (int j = 1; j < nb; j++) {
+			File d = new File(baseDirNames[j], dirName);
+			if (d.isDirectory()) {
+				dirs[j] = new Directory(d);
+				dirs[j].readDir();
+			}
+		}
+
 		// go through all file classes
-		for (int nc = 0; nc < FileOps.NUM_CLASSES; nc++) {
-			int fc = cache.getFileClasses()[nc];
-			//logger.debug("filtering directory "+dir.getPath()+" for class "+fc);
-			File[] fl = FileOps.listFiles(allFiles, FileOps.filterForClass(fc));
-			//logger.debug("  done");
+		for (int classIdx = 0; classIdx < FileOps.NUM_CLASSES; classIdx++) {
+			int fileClass = cache.getFileClasses()[classIdx];
+			//logger.debug("filtering directory "+dir.getPath()+" for class
+			// "+fc);
+			File[] fileList = FileOps.listFiles(allFiles, FileOps
+					.filterForClass(fileClass));
+			//logger.debug(" done");
 			// number of files in the directory
-			int nf = fl.length;
-			if (nf > 0) {
+			int numFiles = fileList.length;
+			if (numFiles > 0) {
 				// create new list
-				list[fc] = new ArrayList(nf);
+				list[fileClass] = new ArrayList(numFiles);
 				// sort the file names alphabetically and iterate the list
-				Arrays.sort(fl);
+				Arrays.sort(fileList);
 				Map hints = FileOps.newHints(FileOps.HINT_BASEDIRS, dirs);
 				hints.put(FileOps.HINT_FILEEXT, scalext);
-				for (int i = 0; i < nf; i++) {
-					DocuDirent f = FileOps.fileForClass(fc, fl[i], hints);
+				for (int i = 0; i < numFiles; i++) {
+					DocuDirent f = FileOps.fileForClass(fileClass, fileList[i],
+							hints);
 					// add the file to our list
-					list[fc].add(f);
+					list[fileClass].add(f);
 					f.setParent(this);
 				}
 			}
 		}
+		// clear the scaled directories
+		for (int j = 1; j < nb; j++) {
+			if (dirs[j] != null) {
+				dirs[j].clearFilenames();
+			}
+		}
+		// update number of cached files if this was the first time
+		if (dirMTime == 0) {
+			cache.numFiles += size();
+		}
 		dirMTime = dir.lastModified();
 		// read metadata as well
 		readMeta();
 		return isValid;
 	}
 
-	/** Check to see if the directory has been modified and reread if necessary.
+	/**
+	 * Check to see if the directory has been modified and reread if necessary.
 	 * 
 	 * @return boolean the directory is valid
 	 */
@@ -245,8 +266,9 @@
 		return isValid;
 	}
 
-	/** Read directory metadata. 
-	 * 
+	/**
+	 * Read directory metadata.
+	 *  
 	 */
 	public void readMeta() {
 		// check for directory metadata...
@@ -255,12 +277,12 @@
 			XMLMetaLoader ml = new XMLMetaLoader();
 			try {
 				// read directory meta file
-				HashMap fileMeta = ml.loadURL(mf.getAbsolutePath());
+				Map fileMeta = ml.loadURL(mf.getAbsolutePath());
 				if (fileMeta == null) {
 					return;
 				}
 				// meta for the directory itself is in the "" bin
-				dirMeta = (HashMap) fileMeta.remove("");
+				dirMeta = (Map) fileMeta.remove("");
 				// read meta for files in this directory
 				readFileMeta(fileMeta, null);
 				// is there meta for other files left?
@@ -281,8 +303,9 @@
 		metaChecked = true;
 	}
 
-	/** Read metadata from all known parent directories.
-	 * 
+	/**
+	 * Read metadata from all known parent directories.
+	 *  
 	 */
 	public void readParentMeta() {
 		// check the parent directories for additional file meta
@@ -299,16 +322,18 @@
 		}
 	}
 
-	/** Read metadata for the files in this directory.
+	/**
+	 * Read metadata for the files in this directory.
 	 * 
-	 * Takes a HashMap with meta-information, adding the relative path
-	 * before the lookup.
+	 * Takes a Map with meta-information, adding the relative path before the
+	 * lookup.
 	 * 
 	 * @param fileMeta
 	 * @param relPath
-	 * @param fc fileClass
+	 * @param fc
+	 *            fileClass
 	 */
-	protected void readFileMeta(HashMap fileMeta, String relPath) {
+	protected void readFileMeta(Map fileMeta, String relPath) {
 		if (list == null) {
 			// there are no files
 			return;
@@ -326,7 +351,7 @@
 				// prepend path to the filename
 				String fn = path + f.getName();
 				// look up meta for this file and remove from dir
-				HashMap meta = (HashMap) fileMeta.remove(fn);
+				Map meta = (Map) fileMeta.remove(fn);
 				if (meta != null) {
 					// store meta in file
 					f.setFileMeta(meta);
@@ -335,7 +360,7 @@
 		}
 	}
 
-	protected void notifyChildMeta(HashMap childmeta) {
+	protected void notifyChildMeta(Map childmeta) {
 		List children = cache.getChildren(this.dirName, true);
 		if (children.size() > 0) {
 			for (Iterator i = children.iterator(); i.hasNext();) {
@@ -345,7 +370,8 @@
 		}
 	}
 
-	/** Update access time.
+	/**
+	 * Update access time.
 	 * 
 	 * @return long time of last access.
 	 */
@@ -355,26 +381,31 @@
 		return t;
 	}
 
-	/** Searches for the file with the name <code>fn</code>.
+	/**
+	 * Searches for the file with the name <code>fn</code>.
 	 * 
-	 * Searches the directory for the file with the name <code>fn</code> and returns 
-	 * its index. Returns -1 if the file cannot be found. 
-	 *  
-	 * @param fn filename
-	 * @param fc file class
+	 * Searches the directory for the file with the name <code>fn</code> and
+	 * returns its index. Returns -1 if the file cannot be found.
+	 * 
+	 * @param fn
+	 *            filename
+	 * @param fc
+	 *            file class
 	 * @return int index of file <code>fn</code>
 	 */
 	public int indexOf(String fn) {
 		int fc = FileOps.classForFilename(fn);
 		return indexOf(fn, fc);
 	}
-	
-	/** Searches for the file with the name <code>fn</code> and class fc.
+
+	/**
+	 * Searches for the file with the name <code>fn</code> and class fc.
 	 * 
-	 * Searches the directory for the file with the name <code>fn</code> and returns 
-	 * its index. Returns -1 if the file cannot be found. 
-	 *  
-	 * @param fn filename
+	 * Searches the directory for the file with the name <code>fn</code> and
+	 * returns its index. Returns -1 if the file cannot be found.
+	 * 
+	 * @param fn
+	 *            filename
 	 * @return int index of file <code>fn</code>
 	 */
 	public int indexOf(String fn, int fc) {
@@ -388,32 +419,37 @@
 		if (list[fc] == null) {
 			return -1;
 		}
-		// linear search -> worst performance
-		int n = list[fc].size();
-		for (int i = 0; i < n; i++) {
-			DocuDirent fs = (DocuDirent) list[fc].get(i);
-			if (fs.getName().equals(fn)) {
-				// filename matches
-				return i;
+		// search for exact match
+		int idx = Collections.binarySearch(list[fc], fn);
+		if (idx >= 0) {
+			return idx;
+		} else {
+			// try closest matches without extension
+			idx = -idx;
+			String fb = FileOps.basename(fn);
+			DocuDirent fs = (DocuDirent) list[fc].get(idx - 1);
+			if (FileOps.basename(fs.getName()).equals(fb)) {
+				// basename matches
+				return idx - 1;
 			}
-		}
-		// try again without extension
-		for (int i = 0; i < n; i++) {
-			DocuDirent fs = (DocuDirent) list[fc].get(i);
-			if (FileOps.basename(fs.getName()).equals(FileOps.basename(fn))) {
+			fs = (DocuDirent) list[fc].get(idx + 1);
+			if (FileOps.basename(fs.getName()).equals(fb)) {
 				// basename matches
-				return i;
+				return idx + 1;
 			}
+
 		}
 		return -1;
 	}
 
-	/** Finds the DocuDirent with the name <code>fn</code>.
+	/**
+	 * Finds the DocuDirent with the name <code>fn</code>.
 	 * 
-	 * Searches the directory for the DocuDirent with the name <code>fn</code> and returns 
-	 * it. Returns null if the file cannot be found. 
-	 *  
-	 * @param fn filename
+	 * Searches the directory for the DocuDirent with the name <code>fn</code>
+	 * and returns it. Returns null if the file cannot be found.
+	 * 
+	 * @param fn
+	 *            filename
 	 * @return DocuDirent
 	 */
 	public DocuDirent find(String fn) {
@@ -425,12 +461,15 @@
 		return null;
 	}
 
-	/** Finds the DocuDirent with the name <code>fn</code> and class <code>fc</code>.
+	/**
+	 * Finds the DocuDirent with the name <code>fn</code> and class
+	 * <code>fc</code>.
 	 * 
-	 * Searches the directory for the DocuDirent with the name <code>fn</code> and returns 
-	 * it. Returns null if the file cannot be found. 
-	 *  
-	 * @param fn filename
+	 * Searches the directory for the DocuDirent with the name <code>fn</code>
+	 * and returns it. Returns null if the file cannot be found.
+	 * 
+	 * @param fn
+	 *            filename
 	 * @return DocuDirent
 	 */
 	public DocuDirent find(String fn, int fc) {
@@ -442,13 +481,16 @@
 	}
 
 	/**
-	 * @return String
+	 * Returns the digilib canonical name.
+	 * 
+	 * @return
 	 */
 	public String getDirName() {
 		return dirName;
 	}
 
-	/** The directory is valid (exists on disk).
+	/**
+	 * The directory is valid (exists on disk).
 	 * 
 	 * @return boolean
 	 */
@@ -456,7 +498,8 @@
 		return isValid;
 	}
 
-	/** The directory has been read from disk.
+	/**
+	 * The directory has been read from disk.
 	 * 
 	 * @return
 	 */
@@ -474,10 +517,10 @@
 	/**
 	 * @return Hashtable
 	 */
-	public HashMap getDirMeta() {
+	public Map getDirMeta() {
 		return dirMeta;
 	}
-	
+
 	/**
 	 * Checks metadata
 	 *  
@@ -499,9 +542,11 @@
 
 	/**
 	 * Sets the dirMeta.
-	 * @param dirMeta The dirMeta to set
+	 * 
+	 * @param dirMeta
+	 *            The dirMeta to set
 	 */
-	public void setDirMeta(HashMap dirMeta) {
+	public void setDirMeta(Map dirMeta) {
 		this.dirMeta = dirMeta;
 	}