Index: /branches/ant-install-branch/gsdl3/src/java/org/greenstone/gsdl3/util/DBInfo.java
===================================================================
--- /branches/ant-install-branch/gsdl3/src/java/org/greenstone/gsdl3/util/DBInfo.java	(revision 9527)
+++ /branches/ant-install-branch/gsdl3/src/java/org/greenstone/gsdl3/util/DBInfo.java	(revision 9527)
@@ -0,0 +1,64 @@
+package org.greenstone.gsdl3.util;
+
+import java.util.Vector;
+import java.util.HashMap;
+import java.util.Set;
+/** class to hold info from a gdbm (or equiv) query
+ * maps a String key to a list of Strings (values)
+ * at the moment, the user must know if something has a single value or not
+ */
+public class DBInfo {
+
+    protected HashMap info_map_;
+
+    public DBInfo() {
+	info_map_ = new HashMap();
+    }
+
+    // methods for keys that can have a single value
+
+    /** set the value for a key - replaces any existing value */
+    public void setInfo(String key, String value) {
+	Vector v = new Vector();
+	v.add(value);
+	info_map_.put(key, v);
+    }
+
+    /** get the value - used for keys that have one value, otherwise just
+     * returns the first one */
+    public String getInfo(String key) {
+	Vector items = (Vector)info_map_.get(key);
+	if (items==null) {
+	    return "";
+	}
+	return (String)items.firstElement();
+    }
+
+    // methods for keys that can have multiple values
+    
+    /** add a value to a key - for keys that can have multiple values */
+    public void addInfo(String key, String value) {
+	Vector v = (Vector)info_map_.get(key);
+	if (v==null) {
+	    v = new Vector();
+	}
+	v.add(value);
+	info_map_.put(key, v);
+    }
+    /** return a vector of all values associated with a key 
+     * @return Vector of Strings
+     */
+    public Vector getMultiInfo(String key) {
+	return (Vector)info_map_.get(key);
+    }
+
+    /** returns a list of all the keys in the info */
+    public Set getKeys() {
+	return info_map_.keySet();
+    }
+    /** returns the Info as a string */
+    public String toString() {
+	return info_map_.toString();
+
+    }
+}
Index: /branches/ant-install-branch/gsdl3/src/java/org/greenstone/gsdl3/util/GDBMWrapper.java
===================================================================
--- /branches/ant-install-branch/gsdl3/src/java/org/greenstone/gsdl3/util/GDBMWrapper.java	(revision 9527)
+++ /branches/ant-install-branch/gsdl3/src/java/org/greenstone/gsdl3/util/GDBMWrapper.java	(revision 9527)
@@ -0,0 +1,236 @@
+package org.greenstone.gsdl3.util;
+
+import au.com.pharos.gdbm.GdbmFile;
+import au.com.pharos.packing.*;
+import au.com.pharos.gdbm.GdbmException;
+
+/** java wrapper class for gdbm - uses Java-GDBM written by Martin Pool
+ * replaces gdbmclass in the old version
+ */
+
+public class GDBMWrapper {
+
+    // 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;
+
+    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
+	    System.err.println("GDBMWrapper: couldn't open database "+filename);
+	    return false;
+	} 
+	db_.setKeyPacking(new StringPacking());
+	db_.setValuePacking(new StringPacking());
+	return true;
+    }
+
+    /** cloase the database associated with this wrapper */
+    public void closeDatabase() {
+	try {
+	    db_.close();
+	} catch (GdbmException e) {
+	    // should never get here - close never actually throws an exception
+	    System.err.println("GDBMWrapper: error on close()");
+	}
+    }
+
+    /** 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) {
+	    System.err.println("GDBMWrapper: couldn't get record");
+	    return null;
+	}
+	if (s_info==null) {
+	    // record not present
+	    System.err.println("GDBMWrapper: key "+main_key+" not present in db");
+	    return null;
+	}
+	DBInfo info = new DBInfo();
+
+	String [] lines = s_info.split("\n");
+	String key;
+	String value;
+	for (int i=0; i<lines.length; i++) {
+	    //System.out.println("line:"+lines[i]);
+	    int a = lines[i].indexOf('<');
+	    int b= lines[i].indexOf('>');
+	    if (a==-1 || b==-1) {
+		System.err.println("error: bad format in db");
+	    }
+	    else {
+		key=lines[i].substring(a+1, b);
+		value=lines[i].substring(b+1);
+		//System.out.println("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) {
+	DBInfo info = getInfo(Long.toString(docnum));
+	String oid = info.getInfo("section");
+	return oid;
+    }
+
+    /** 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) 
+     * a suffix is expected to be present so test before using 
+     * TODO: fc, lc, ns, ps, .rt .ss*/
+    public String translateOID(String oid) {
+
+	int p = oid.lastIndexOf('.');
+	if (p != oid.length()-3) {
+	    System.out.println("translateoid error: '.' is not the third to last char!!");
+	    return oid;
+	}
+	
+	String top = oid.substring(0, p);
+	String suff = oid.substring(p+1);
+	if (suff.equals("pr")) {
+	    return OID.getParent(top);
+	} else if (suff.equals("rt")) {
+	    return OID.getTop(top);
+	} else {
+	    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);
+	    }
+	    DBInfo info = getInfo(doc_id);
+	    if (info==null) {
+		System.out.println("info is null!!");
+		return top;
+	    }
+
+
+	    /** ATTACK OF THE RANDOM BLARG!!!! **/
+	    /** All your code are belong to me! **/
+
+	    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;
+    }
+}
