Index: /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/FlatDatabaseWrapper.java
===================================================================
--- /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/FlatDatabaseWrapper.java	(revision 15325)
+++ /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/FlatDatabaseWrapper.java	(revision 15325)
@@ -0,0 +1,41 @@
+/*
+ *    FlatDatabaseWrapper.java
+ *    Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ *
+ *    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.
+ *
+ *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.greenstone.gsdl3.util;
+
+public interface FlatDatabaseWrapper {
+
+  /* just read access, many readers can share a database */
+  public final static int READ = 0;
+  /* read/write, exclusive access */
+  public final static int WRITE = 1;
+
+  // do we want the other modes? 
+  // -- read/write - create if doesn't exist 
+  // -- read/write - create a new database
+  /** open database named filename, using mode */
+  public boolean openDatabase(String filename, int mode);
+
+  /** close the database associated with this wrapper */
+  public void closeDatabase();
+
+  /** returns the value associated with the key */
+  public String getValue(String key);
+
+}
+
Index: /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/GDBMWrapper.java
===================================================================
--- /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/GDBMWrapper.java	(revision 15324)
+++ /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/GDBMWrapper.java	(revision 15325)
@@ -5,5 +5,5 @@
 import au.com.pharos.gdbm.GdbmException;
 
-import org.apache.log4j.*;
+import org.apache.log4j.Logger;
 
 /** java wrapper class for gdbm - uses Java-GDBM written by Martin Pool
@@ -11,289 +11,94 @@
  */
 
-public class GDBMWrapper {
+public class GDBMWrapper 
+  implements FlatDatabaseWrapper {
 
-      static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.GDBMWrapper.class.getName());
-
-
-    // Values must match those from gdbm.h - uses the definitions from GdbmFile !
-    /** Indicates that the caller will just read the database.  Many
-     * readers may share a database. */
-    public final static int READER = GdbmFile.READER;
- 
-    /** The caller wants read/write access to an existing database and
-     * requires exclusive access. */
-    public final static int WRITER = GdbmFile.WRITER;
- 
-    /** The caller wants exclusive read/write access, and the database
-     * should be created if it does not already exist. */
-    public final static int WRCREAT = GdbmFile.WRCREAT;
- 
-    /** The caller wants exclusive read/write access, and the database
-     * should be replaced if it already exists. */
-    public final static int NEWDB = GdbmFile.NEWDB;
+  static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.GDBMWrapper.class.getName());
+  
+  /* GdbmFile modes:
+     READER - read access, many readers may share the database
+     WRITER - read/write access, exclusive access
+     WRCREAT - read/write access, create db if doesn't exist
+     NEWDB - read/write access, db should be replaced if exists
+  */
+  
 
     protected GdbmFile db_=null;
 
-    /** open the database filename, with mode mode - uses the constants
-	above, eg GdbmFile.WRITER */
-    public boolean openDatabase(String filename, int mode){
-	try {
-	    if (db_!=null) {
-		db_.close();
-	    }
-	    db_ = new GdbmFile(filename, mode);
-	} catch ( GdbmException e) { // the database wasn't opened or created
-	    logger.error("couldn't open database "+filename);
-	    return false;
-	} 
-	db_.setKeyPacking(new StringPacking());
-	db_.setValuePacking(new StringPacking());
-	return true;
+  /** open the database filename, with mode mode - uses the constants
+      above, eg GdbmFile.WRITER */
+  public boolean openDatabase(String filename, int mode) {
+    // need to convert mode to GdbmFile mode
+    if (mode == READ) { 
+      mode = GdbmFile.READER;
+    } else if (mode == WRITE) {
+      mode = GdbmFile.WRITER;
+    }	else {
+      logger.error ("invalid mode, "+mode+ ", opening db for reading only");
+      mode = GdbmFile.READER;
     }
+      
+    try {
+      if (db_!=null) {
+	db_.close();
+      }
+      db_ = new GdbmFile(filename, mode);
+    } catch ( GdbmException e) { // the database wasn't opened or created
+      logger.error("couldn't open database "+filename);
+      return false;
+    } 
+    db_.setKeyPacking(new StringPacking());
+    db_.setValuePacking(new StringPacking());
+    return true;
+  }
+  
+  /** close the database associated with this wrapper */
+  public void closeDatabase() {
+    try {
+      if (db_ != null) {
+	db_.close();
+	db_ = null;
+      }
+    } catch (GdbmException e) {
+      // should never get here - close never actually throws an exception
+      logger.error("error on close()");
+    }
+  }
 
-    /** close the database associated with this wrapper */
-    public void closeDatabase() {
-	try {
-	    if (db_ != null) {
-		db_.close();
-		db_ = null;
-	    }
-	} catch (GdbmException e) {
-	    // should never get here - close never actually throws an exception
-	    logger.error("error on close()");
-	}
+  public String getValue(String key) {
+    if (db_==null) {
+      return null;
     }
+    String s_info;
+    try {
+      s_info = (String)db_.fetch(key);
+    } catch (GdbmException e) {
+      logger.error("couldn't get record");
+      return null;
+    }
+    if (s_info==null) {
+      // record not present
+      logger.error("key "+key+" not present in db");
+      return null;
+    }
+    return s_info;
+  }
 
-    /** returns a DBInfo object containing all the name-value pairs for 
-     * main_key 
-     * @see DBInfo
-     */
-    public DBInfo getInfo(String main_key) {
-	if (db_==null) {
-	    return null;
-	}
-	String s_info;
-	try {
-	    s_info = (String)db_.fetch(main_key);
-	} catch (GdbmException e) {
-	    logger.error("couldn't get record");
-	    return null;
-	}
-	if (s_info==null) {
-	    // record not present
-	    logger.error("key "+main_key+" not present in db");
-	    return null;
-	}
-	DBInfo info = new DBInfo();
+  /** sets the key value as info 
+   * TODO - not implemented yet */
+  public boolean setValue (String key, String value) {
+    if (db_==null) {
+      return false;
+    }
+    return false;
+  }
+  /** deletes the entry for key 
+   * TODO - not implemented yet */
+  public boolean deleteKey(String key) {
+    if (db_==null) {
+      return false;
+    }
+    return false;
+  }
 
-	String [] lines = s_info.split("\n");
-	String key;
-	String value;
-	for (int i=0; i<lines.length; i++) {
-	    logger.debug("line:"+lines[i]);
-	    int a = lines[i].indexOf('<');
-	    int b= lines[i].indexOf('>');
-	    if (a==-1 || b==-1) {
-		logger.error("bad format in db");
-	    }
-	    else {
-		key=lines[i].substring(a+1, b);
-		value=lines[i].substring(b+1);
-		logger.debug("key="+key+", val="+value);
-		info.addInfo(key, value);
-
-	    }
-	}
-	return info;
-    }
-
-    /** sets all the name-value pairs in info as the content for key key 
-     * TODO - not implemented yet */
-    public boolean setInfo(String key, DBInfo info) {
-	if (db_==null) {
-	    return false;
-	}
-	return true;
-    }
-    /** sets the key value as info 
-     * TODO - not implemented yet */
-    public boolean setInfo (String key, String info) {
-	if (db_==null) {
-	    return false;
-	}
-	return true;
-    }
-    /** deletes the entry for key 
-     * TODO - not implemented yet */
-    public boolean deleteKey(String key) {
-	if (db_==null) {
-	    return false;
-	}
-	return true;
-    }
-
-    // greenstone convenience methods - should these go into a separate class?
-    // yes I think so.
-    /** converts a greenstone OID to internal docnum */
-    public long OID2Docnum(String OID) {
-	DBInfo info = getInfo(OID);
-	if (info != null) {
-	    long real_num = Long.parseLong(info.getInfo("docnum"));
-	    return real_num;
-	}
-	return -1;
-    }
-    /** converts a docnum to greenstone OID */
-    public String docnum2OID(long docnum) {
-	return docnum2OID(Long.toString(docnum));
-    }
-    
-    /** converts a docnum to greenstone OID */
-    public String docnum2OID(String docnum) {
-	DBInfo info = getInfo(docnum);
-	if (info!=null){
-	    String oid = info.getInfo("section");
-	    return oid;
-	}else{
-	    return null;
-	}
-    }
-
-    /** converts an external id to greenstone OID */
-    public String externalId2OID(String extid) {
-	DBInfo info = getInfo(extid);
-	if (info != null) {
-	    String oid = info.getInfo("section");
-	    return oid;
-	}
-	return null;
-    }
-    /** translates relative oids into proper oids:
-     * .pr (parent), .rt (root) .fc (first child), .lc (last child),
-     * .ns (next sibling), .ps (previous sibling) 
-     * .np (next page), .pp (previous page) : links sections in the order that you'd read the document
-     * a suffix is expected to be present so test before using 
-     */
-    public String translateOID(String oid) {
-
-	int p = oid.lastIndexOf('.');
-	if (p != oid.length()-3) {
-	    logger.info("translateoid error: '.' is not the third to last char!!");
-	    return oid;
-	}
-	
-	String top = oid.substring(0, p);
-	String suff = oid.substring(p+1);
-	// just in case we have multiple extensions, we must translate
-	// we process inner ones first
-	if (OID.needsTranslating(top)) {
-	    top = translateOID(top);
-	}
-	if (suff.equals("pr")) {
-	    return OID.getParent(top);
-	} 
-	if (suff.equals("rt")) {
-	    return OID.getTop(top);
-	} 
-	if (suff.equals("np")) {
-	    // try first child
-	    String node_id = translateOID(top+".fc");
-	    if (!node_id.equals(top)) {
-		return node_id;
-	    }
-	    // try next sibling
-	    node_id = translateOID(top+".ns");
-	    if (!node_id.equals(top)) {
-		return node_id;
-	    }
-	    // otherwise we keep trying parents sibling
-	    String child_id = top;
-	    String parent_id = OID.getParent(child_id);
-	    while(!parent_id.equals(child_id)) {
-		node_id = translateOID(parent_id+".ns");
-		if (!node_id.equals(parent_id)) {
-		    return node_id;
-		}
-		child_id = parent_id;
-		parent_id = OID.getParent(child_id);
-	    }
-	    return top; // we couldn't get a next page, so just return the original
-	} 
-	if (suff.equals("pp")) {
-	    String prev_sib = translateOID(top+".ps");
-	    if (prev_sib.equals(top)) {
-		// no previous sibling, so return the parent
-		return OID.getParent(top);
-	    }
-	    // there is a previous sibling, so its either this section, or the last child of the last child..
-	    String last_child = translateOID(prev_sib+".lc");
-	    while (!last_child.equals(prev_sib)) {
-		prev_sib = last_child;
-		last_child = translateOID(prev_sib+".lc");
-	    }
-	    return last_child;
-	} 
-	
-	int sibling_num = 0;
-	if (suff.equals("ss")) {
-	    // we have to remove the sib num before we get top
-	    p = top.lastIndexOf('.');
-	    sibling_num = Integer.parseInt(top.substring(p+1));
-	    top = top.substring(0, p);
-	}
-	
-	// need to get info out of gdbm db -
-	String doc_id = top;
-	if (suff.endsWith("s")) {
-	    doc_id = OID.getParent(top);
-	    if (doc_id.equals(top)) {
-		// i.e. we are already at the top
-		return top;
-	    }
-	}
-	DBInfo info = getInfo(doc_id);
-	if (info==null) {
-	    logger.info("info is null!!");
-	    return top;
-	}
-	
-	String contains = info.getInfo("contains");
-	if (contains.equals("")) {
-	    // something is wrong
-	    return top;
-	}
-	contains = contains.replaceAll("\"", doc_id);
-	String [] children = contains.split(";");
-	if (suff.equals("fc")) {
-	    return children[0];
-	} else if (suff.equals("lc")) {
-	    return children[children.length-1];
-	} else {
-	    if (suff.equals("ss")) {
-		return children[sibling_num-1];
-	    }
-	    // find the position that we are at.
-	    int i=0;
-	    while (i<children.length) {
-		if (children[i].equals(top)) {
-		    break;
-		}
-		i++;
-	    }
-	    
-	    if (suff.equals("ns")) {
-		if (i==children.length-1) {
-		    return children[i];
-		}
-		return children[i+1];
-	    } else if (suff.equals("ps")) {
-		if (i==0) {
-		    return children[i];
-		}
-		return children[i-1];
-	    }
-	}
-	
-	return top;
-    }
 }
Index: /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/JDBMWrapper.java
===================================================================
--- /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/JDBMWrapper.java	(revision 15325)
+++ /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/JDBMWrapper.java	(revision 15325)
@@ -0,0 +1,44 @@
+/*
+ *    JDBMWrapper.java
+ *    Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ *
+ *    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.
+ *
+ *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.greenstone.gsdl3.util;
+
+import org.apache.log4j.Logger;
+
+public class JDBMWrapper
+  implements FlatDatabaseWrapper {
+  
+  static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.JDBMWrapper.class.getName());
+
+  /** open database named filename, using mode */
+  public boolean openDatabase(String filename, int mode) {
+    return false;
+  }
+
+
+  /** close the database associated with this wrapper */
+  public void closeDatabase() {
+
+  }
+
+  /** returns the value associated with the key */
+  public String getValue(String key) {
+    return null;
+  }
+
+}
Index: /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/SimpleCollectionDatabase.java
===================================================================
--- /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/SimpleCollectionDatabase.java	(revision 15325)
+++ /greenstone3/trunk/src/java/org/greenstone/gsdl3/util/SimpleCollectionDatabase.java	(revision 15325)
@@ -0,0 +1,264 @@
+/*
+ *    SimpleCollectionDatabase.java
+ *    Copyright (C) 2008 New Zealand Digital Library, http://www.nzdl.org
+ *
+ *    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.
+ *
+ *    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.
+ *
+ *    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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+package org.greenstone.gsdl3.util;
+
+import org.apache.log4j.*;
+
+public class SimpleCollectionDatabase {
+  
+  static Logger logger = Logger.getLogger(org.greenstone.gsdl3.util.SimpleCollectionDatabase.class.getName());
+
+  /* just read access, many readers can share a database */
+  public final static int READ = FlatDatabaseWrapper.READ;
+  /* read/write, exclusive access */
+  public final static int WRITE = FlatDatabaseWrapper.WRITE;
+  
+  protected FlatDatabaseWrapper coll_db = null;
+  
+  public SimpleCollectionDatabase(String db_type) {
+    if (db_type.equalsIgnoreCase("gdbm")) {
+      this.coll_db = new GDBMWrapper();
+    }
+    else if (db_type.equalsIgnoreCase("jdbm")) {
+      this.coll_db = new JDBMWrapper();
+    }
+    else {
+      logger.error("Couldn't create SimpleCollectionDatabase of type "+db_type);
+    }   
+  }
+  
+  /** open the database filename, with mode mode - uses the FlatDatabaseWrapper modes */
+  public boolean openDatabase(String filename, int mode){
+    return this.coll_db.openDatabase(filename, mode);
+  }
+  
+  /** close the database */
+  public void closeDatabase() {
+    this.coll_db.closeDatabase();
+  }
+  
+  /** Returns a DBInfo structure of the key-value pairs associated with a 
+    particular main key in the database */
+  public DBInfo getInfo(String main_key) {
+    String key_info = this.coll_db.getValue(main_key);
+    if (key_info == null || key_info.equals("")) {
+      return null;
+    }
+    
+    DBInfo info = new DBInfo();
+    
+    String [] lines = key_info.split("\n");
+    String key;
+    String value;
+    for (int i=0; i<lines.length; i++) {
+      logger.debug("line:"+lines[i]);
+      int a = lines[i].indexOf('<');
+      int b= lines[i].indexOf('>');
+      if (a==-1 || b==-1) {
+	logger.error("bad format in db");
+      }
+      else {
+	key=lines[i].substring(a+1, b);
+	value=lines[i].substring(b+1);
+	logger.debug("key="+key+", val="+value);
+	info.addInfo(key, value);
+
+      }
+    }
+    return info;
+    
+  }
+  
+  /** converts a greenstone OID to internal docnum */
+  public String OID2Docnum(String OID) {
+    DBInfo info = getInfo(OID);
+    if (info != null) {
+      return info.getInfo("docnum");
+    }
+    return null;
+  }
+    
+  /** converts a greenstone OID to an internal docnum, returning a Long 
+   - convenience method*/
+  public long OID2DocnumLong(String OID) {
+    DBInfo info = getInfo(OID);
+    if (info != null) {
+      long real_num = Long.parseLong(info.getInfo("docnum"));
+      return real_num;
+    }
+    return -1;
+  }
+
+
+  /** converts a docnum to greenstone OID */
+  public String docnum2OID(String docnum) {
+    DBInfo info = getInfo(docnum);
+    if (info!=null){
+      String oid = info.getInfo("section");
+      return oid;
+    } else{
+      return null;
+    }
+  }
+
+  /** converts a docnum to greenstone OID 
+      - convenience method */
+  public String docnum2OID(long docnum) {
+    return docnum2OID(Long.toString(docnum));
+  }
+
+  /** converts an external id to greenstone OID */
+  public String externalId2OID(String extid) {
+    DBInfo info = getInfo(extid);
+    if (info != null) {
+      String oid = info.getInfo("section");
+      return oid;
+    }
+    return null;
+  }
+  
+  /** translates relative oids into proper oids:
+   * .pr (parent), .rt (root) .fc (first child), .lc (last child),
+   * .ns (next sibling), .ps (previous sibling) 
+   * .np (next page), .pp (previous page) : links sections in the order that you'd read the document
+   * a suffix is expected to be present so test before using 
+   */
+  public String translateOID(String oid) {
+
+    int p = oid.lastIndexOf('.');
+    if (p != oid.length()-3) {
+      logger.info("translateoid error: '.' is not the third to last char!!");
+      return oid;
+    }
+	
+    String top = oid.substring(0, p);
+    String suff = oid.substring(p+1);
+    // just in case we have multiple extensions, we must translate
+    // we process inner ones first
+    if (OID.needsTranslating(top)) {
+      top = translateOID(top);
+    }
+    if (suff.equals("pr")) {
+      return OID.getParent(top);
+    } 
+    if (suff.equals("rt")) {
+      return OID.getTop(top);
+    } 
+    if (suff.equals("np")) {
+      // try first child
+      String node_id = translateOID(top+".fc");
+      if (!node_id.equals(top)) {
+	return node_id;
+      }
+      // try next sibling
+      node_id = translateOID(top+".ns");
+      if (!node_id.equals(top)) {
+	return node_id;
+      }
+      // otherwise we keep trying parents sibling
+      String child_id = top;
+      String parent_id = OID.getParent(child_id);
+      while(!parent_id.equals(child_id)) {
+	node_id = translateOID(parent_id+".ns");
+	if (!node_id.equals(parent_id)) {
+	  return node_id;
+	}
+	child_id = parent_id;
+	parent_id = OID.getParent(child_id);
+      }
+      return top; // we couldn't get a next page, so just return the original
+    } 
+    if (suff.equals("pp")) {
+      String prev_sib = translateOID(top+".ps");
+      if (prev_sib.equals(top)) {
+	// no previous sibling, so return the parent
+	return OID.getParent(top);
+      }
+      // there is a previous sibling, so its either this section, or the last child of the last child..
+      String last_child = translateOID(prev_sib+".lc");
+      while (!last_child.equals(prev_sib)) {
+	prev_sib = last_child;
+	last_child = translateOID(prev_sib+".lc");
+      }
+      return last_child;
+    } 
+	
+    int sibling_num = 0;
+    if (suff.equals("ss")) {
+      // we have to remove the sib num before we get top
+      p = top.lastIndexOf('.');
+      sibling_num = Integer.parseInt(top.substring(p+1));
+      top = top.substring(0, p);
+    }
+	
+    // need to get info out of collection db -
+    String doc_id = top;
+    if (suff.endsWith("s")) {
+      doc_id = OID.getParent(top);
+      if (doc_id.equals(top)) {
+	// i.e. we are already at the top
+	return top;
+      }
+    }
+    DBInfo info = getInfo(doc_id);
+    if (info==null) {
+      logger.info("info is null!!");
+      return top;
+    }
+	
+    String contains = info.getInfo("contains");
+    if (contains.equals("")) {
+      // something is wrong
+      return top;
+    }
+    contains = contains.replaceAll("\"", doc_id);
+    String [] children = contains.split(";");
+    if (suff.equals("fc")) {
+      return children[0];
+    } else if (suff.equals("lc")) {
+      return children[children.length-1];
+    } else {
+      if (suff.equals("ss")) {
+	return children[sibling_num-1];
+      }
+      // find the position that we are at.
+      int i=0;
+      while (i<children.length) {
+	if (children[i].equals(top)) {
+	  break;
+	}
+	i++;
+      }
+	    
+      if (suff.equals("ns")) {
+	if (i==children.length-1) {
+	  return children[i];
+	}
+	return children[i+1];
+      } else if (suff.equals("ps")) {
+	if (i==0) {
+	  return children[i];
+	}
+	return children[i-1];
+      }
+    }
+	
+    return top;
+  }
+}
