Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/Action.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/Action.java	(revision 24253)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/Action.java	(revision 24254)
@@ -77,5 +77,5 @@
     }
 
-	protected void extractMetadataNames(Element format, HashSet meta_names) {
+    protected void extractMetadataNames(Element format, HashSet meta_names) {
     //NodeList nodes = format.getElementsByTagNameNS("metadata", "http://www.greenstone.org/configformat");
     NodeList metadata_nodes = format.getElementsByTagName("gsf:metadata");
@@ -90,5 +90,9 @@
                 metadata.append("all");
                 metadata.append(GSConstants.META_RELATION_SEP);
-            }
+            } else if (all.equals("offset")) { // multiple is no longer boolean. 
+		// Can be "true", "false" or it can be "offset" (when requested to use mdoffset)
+                metadata.append("offset");
+                metadata.append(GSConstants.META_RELATION_SEP);
+            } // if multiple=false, then get first value for the metadata
             if (!select.equals("")) {
                 metadata.append(select);
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/GS2BrowseAction.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/GS2BrowseAction.java	(revision 24253)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/GS2BrowseAction.java	(revision 24254)
@@ -272,4 +272,7 @@
 		Element d = this.doc.createElement(GSXML.DOC_NODE_ELEM);
 		d.setAttribute(GSXML.NODE_ID_ATT, ((Element)doc_nodes.item(c)).getAttribute(GSXML.NODE_ID_ATT));
+		if(((Element)doc_nodes.item(c)).hasAttribute(GSXML.NODE_MDOFFSET_ATT)) {
+		    d.setAttribute(GSXML.NODE_MDOFFSET_ATT, ((Element)doc_nodes.item(c)).getAttribute(GSXML.NODE_MDOFFSET_ATT));
+		}
 		doc_list.appendChild(d);
 	    }
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractBrowse.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractBrowse.java	(revision 24253)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractBrowse.java	(revision 24254)
@@ -574,5 +574,5 @@
      * <docNode nodeId='xxx' nodeType='leaf' docType='hierarchy'/>
      */
-    protected Element createDocNode(String node_id) {
+    protected Element createDocNode(String node_id, int offset) {
 	Element node = this.doc.createElement(GSXML.DOC_NODE_ELEM);
 	node.setAttribute(GSXML.NODE_ID_ATT, node_id);
@@ -587,4 +587,8 @@
 	String node_type = getNodeType(node_id, doc_type);	
 	node.setAttribute(GSXML.NODE_TYPE_ATT, node_type);
+	
+	// mdoffset information stored in DB determines which of multiple values for
+	// a metadata needs to be displayed when a classifier is built on that metadata
+	node.setAttribute(GSXML.NODE_MDOFFSET_ATT, Integer.toString(offset));
 	return node;
     }
@@ -621,9 +625,25 @@
 	ArrayList child_ids = getChildrenIds(node_id);
 	if (child_ids==null) return;
+	
+	// get a list of all mdoffsets at this point
+	ArrayList child_offsets = getOffsetsForChildrenIds(node_id);
+	// make sure that if it's doesn't match up with child_ids in length, 
+	// it is padded with 0s to make up the length
+	if(child_offsets == null) {
+	    child_offsets = new ArrayList(child_ids.size());
+	} 
+	if(child_offsets.size() < child_ids.size()) {
+	    Integer zero = new Integer(0);
+	    for(int i = child_offsets.size()-1; i < child_ids.size(); i++) {
+		child_offsets.add(zero);
+	    }
+	}
+
 	for (int i=0; i< child_ids.size(); i++) {
 	    String child_id = (String)child_ids.get(i);
+	    int child_offset = ((Integer)child_offsets.get(i)).intValue(); // counts both docnodes and classnodes at the childlevel, is this right?
 	    Element child_elem;
 	    if (isDocumentId(child_id)) {
-		child_elem = createDocNode(child_id);
+		child_elem = createDocNode(child_id, child_offset);
 	    } else {
 		child_elem = createClassifierNode(child_id);
@@ -684,4 +704,9 @@
     }
 
+    /** Override to get a list of mdoffsets of children. Return null if no children or if no mdoffsets for them */
+    protected ArrayList getOffsetsForChildrenIds(String node_id) { 
+	return null; 
+    }
+
     /** if id ends in .fc, .pc etc, then translate it to the correct id */
     abstract protected String translateId(String node_id);
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractDocumentRetrieve.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractDocumentRetrieve.java	(revision 24253)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractDocumentRetrieve.java	(revision 24254)
@@ -241,4 +241,21 @@
 	    Element request_node = (Element) request_nodes.item(i);
 	    String node_id = request_node.getAttribute(GSXML.NODE_ID_ATT);
+
+	    // make a custom copy of metadata_names_list for each docID, since mdoffset value varies for each doc
+	    ArrayList customised_metadata_names_list = new ArrayList(metadata_names_list.size());
+	    int mdoffset = 0;
+	    if(request_node.hasAttribute(GSXML.NODE_MDOFFSET_ATT)) {
+		String offset = request_node.getAttribute(GSXML.NODE_MDOFFSET_ATT);
+		mdoffset = Integer.parseInt(offset);
+		
+		for(int x = 0; x < metadata_names_list.size(); x++) {
+		    String metaname = (String)metadata_names_list.get(x);
+		    if(metaname.indexOf("offset" + GSConstants.META_RELATION_SEP) != -1) {
+			// append offset number to the metaname
+			metaname = metaname.replace("offset"+GSConstants.META_RELATION_SEP, "offset"+mdoffset+GSConstants.META_RELATION_SEP);
+		    } 
+		    customised_metadata_names_list.add(x, metaname);		    
+		}
+	    }
 	   
 	    boolean is_external_link = false;
@@ -272,5 +289,5 @@
 	    if (!is_external_link){
 		try {
-		    Element metadata_list = getMetadataList(node_id, all_metadata, metadata_names_list);
+		    Element metadata_list = getMetadataList(node_id, all_metadata, customised_metadata_names_list);
 		    request_node.appendChild(metadata_list);
 		} catch (GSException e) {		
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractGS2DocumentRetrieve.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractGS2DocumentRetrieve.java	(revision 24253)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/AbstractGS2DocumentRetrieve.java	(revision 24254)
@@ -41,4 +41,6 @@
 import java.util.Iterator;
 import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import org.apache.log4j.*;
@@ -196,7 +198,17 @@
 	    
 	} else {
+	    // prepare regex to work with mdoffset: looking for <offset\d*_>
+	    Pattern pattern = Pattern.compile("offset[0-9]*" + GSConstants.META_RELATION_SEP);
+
 	    for (int i=0; i<metadata_names.size(); i++) {
                 String meta_name = (String) metadata_names.get(i);
 		String value = getMetadata(node_id, info, meta_name, lang);
+
+		// Remove the occurrence (if any) in this metaname of the mdoffset number in the pattern <offset\d*_>
+		// Leaving string "offset" in at this point: it will be handled in config_format.xsl's gsf:metadata template match
+		Matcher matcher = pattern.matcher(meta_name);
+		meta_name = matcher.replaceFirst("offset" + GSConstants.META_RELATION_SEP); 
+		    //replaceFirst(""); // if removing the occurrence (if any) of entire pattern <offset\d*_> in input		
+
 		GSXML.addMetadata(this.doc, metadata_list, meta_name, value);
 	    }
@@ -317,5 +329,5 @@
     protected String getMetadata(String node_id, DBInfo info, 
 				 String metadata, String lang) {
-	boolean multiple = false;
+	String multiple = "false"; // multiple can now be "true", "false" or "offset<number>". It's no longer a boolean
 	String relation = "";
 	String separator = ", ";
@@ -346,6 +358,6 @@
 	metadata = metadata.substring(pos+1);
 	// check for all on the front
-	if (temp.equals("all")) {
-	    multiple=true;	    
+	if (temp.equals("all") || temp.startsWith("offset")) { // multiple can now be "true", "false" or "offset"
+	    multiple = temp; // multiple=true;
 	    pos = metadata.indexOf(GSConstants.META_RELATION_SEP);
 	    if (pos ==-1) {
@@ -399,10 +411,18 @@
 	StringBuffer result = new StringBuffer();
 
-	if (!multiple) {
+	if (multiple.equals("false")) {
 	    result.append(this.macro_resolver.resolve(relation_info.getInfo(metadata), lang, MacroResolver.SCOPE_META, relation_id));
-	} else {
-	    // we have multiple meta
+	} else if(multiple.startsWith("offset")) { // multiple = offset
+	    String offset = multiple.substring("offset".length(), multiple.length());
+	    int offsetVal = offset.equals("") ? 0 : Integer.parseInt(offset);
+	    String value = relation_info.getInfoOffset(metadata, offsetVal); // what if this metadata is not the one we need to get the offset for? MDTYPE!
+	       // at the moment, do we assume the user will specify retrieving the offset only for such metadata as has an offset? 
+	       // At least, getInfoOffset will return the firstelement if the offset exceeds the bounds of the values for this metadata key
+
+	    result.append(this.macro_resolver.resolve(value, lang, MacroResolver.SCOPE_META, relation_id));
+	    
+	} else { // multiple = true, we have multiple meta	    
 	    Vector values = relation_info.getMultiInfo(metadata);
-  	    if (values != null) {
+	    if (values != null) {
 		boolean first = true;
 		for (int i=0; i<values.size(); i++) {
@@ -415,5 +435,5 @@
 		}
 	    }
-          logger.info(result); 
+	    logger.info(result);
 	}
 	// if not ancestors, then this is all we do
@@ -428,5 +448,5 @@
 	    relation_info = this.coll_db.getInfo(relation_id);
 	    if (relation_info == null) return result.toString();
-	    if (!multiple) {
+	    if (multiple.equals("false")) { //if (!multiple)
 		result.insert(0, separator);
 		result.insert(0, this.macro_resolver.resolve(relation_info.getInfo(metadata), lang, MacroResolver.SCOPE_META, relation_id));
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Browse.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Browse.java	(revision 24253)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/GS2Browse.java	(revision 24254)
@@ -175,4 +175,26 @@
 
     }
+    /** returns a list of the mdoffset values in order for the children, null is 
+      * returned if there are no children or if the mdoffset field is empty */
+    protected ArrayList getOffsetsForChildrenIds(String node_id) { 
+	DBInfo info = this.coll_db.getInfo(node_id);
+	if (info == null) {
+	    return null;
+	}
+	
+	ArrayList childrenOffsets = new ArrayList();
+	
+	String offset = info.getInfo("mdoffset").trim();
+	if(offset.equals("")) {
+	    return null;
+	}
+	StringTokenizer st = new StringTokenizer(offset, ";");
+	while (st.hasMoreTokens()) {
+	    String val = st.nextToken().trim();
+	    childrenOffsets.add(Integer.valueOf(val));
+	}
+	return childrenOffsets;
+    }
+
     /** returns the node id of the parent node, null if no parent */
     protected String getParentId(String node_id){
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/DBInfo.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/DBInfo.java	(revision 24253)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/DBInfo.java	(revision 24254)
@@ -53,4 +53,18 @@
     }
 
+    /** get the value for the key at offset. If offset out of bounds,
+     * just return the first one. */
+    public String getInfoOffset(String key, int offset) {
+	Vector items = (Vector)info_map_.get(key);
+	if (items==null) {
+	    return "";
+	}
+	if(offset >= 0 && offset < items.size()) {
+	    return (String)items.get(offset);
+	} // else
+	return (String)items.firstElement();
+    }
+
+
     // methods for keys that can have multiple values
     
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/GSXML.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/GSXML.java	(revision 24253)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/GSXML.java	(revision 24254)
@@ -157,4 +157,5 @@
   public static final String NODE_TYPE_INTERNAL = "internal";
   public static final String NODE_TYPE_LEAF = "leaf";
+  public static final String NODE_MDOFFSET_ATT = "mdoffset";
   
   public static final String DOC_TYPE_SIMPLE = "simple";
@@ -1039,5 +1040,5 @@
       StringWriter sw = new StringWriter();
       trans.transform(new DOMSource(e), new StreamResult(sw));
-      System.err.println( sw.toString() );
+      System.err.println( sw.toString() ); // logger.info( sw.toString() );
     } catch( Exception ex ) {
       System.err.println( "couldn't write " + e + " to log" );
Index: /main/trunk/greenstone3/web/interfaces/default/transform/config_format.xsl
===================================================================
--- /main/trunk/greenstone3/web/interfaces/default/transform/config_format.xsl	(revision 24253)
+++ /main/trunk/greenstone3/web/interfaces/default/transform/config_format.xsl	(revision 24254)
@@ -34,5 +34,5 @@
 	<a><xslt:attribute name='href'><xslt:value-of select='$library_name'/>?a=b&amp;rt=r&amp;s=<xslt:value-of select='$serviceName'/>&amp;c=<xslt:value-of select='$collName'/>&amp;cl=<xslt:value-of select='@nodeID'/><xslt:if test="classifierNode|documentNode">.pr</xslt:if><xslt:if test="parent::node()[@orientation='horizontal']">&amp;sib=1</xslt:if></xslt:attribute>
 	  <xsl:apply-templates/>
-	</a>
+	</a>	
       </xsl:when>
 	  <xsl:when test="@type='source'">
@@ -112,5 +112,5 @@
 
   <xsl:template match="gsf:metadata" mode="get-metadata-name">
-    <xsl:if test="@multiple='true'">all_</xsl:if><xsl:if test='@select'><xsl:value-of select='@select'/>_</xsl:if><xsl:if test="@separator">*<xsl:value-of select='@separator'/>*_</xsl:if><xsl:value-of select="@name"/>
+    <xsl:if test="@multiple='true'">all_</xsl:if><xsl:if test="starts-with(@multiple,'offset')"><xsl:value-of select='@multiple'/>_</xsl:if><xsl:if test='@select'><xsl:value-of select='@select'/>_</xsl:if><xsl:if test="@separator">*<xsl:value-of select='@separator'/>*_</xsl:if><xsl:value-of select="@name"/>
   </xsl:template>
   
