Index: /main/trunk/greenstone3/CollectionGroups.java
===================================================================
--- /main/trunk/greenstone3/CollectionGroups.java	(revision 31285)
+++ /main/trunk/greenstone3/CollectionGroups.java	(revision 31285)
@@ -0,0 +1,437 @@
+package org.greenstone.gsdl3.service;
+
+import java.io.File;
+
+import org.apache.log4j.Logger;
+import org.greenstone.gsdl3.util.GSFile;
+import org.greenstone.gsdl3.util.GSXML;
+import org.greenstone.gsdl3.util.UserContext;
+import org.greenstone.gsdl3.util.XMLConverter;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.Set;
+
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import java.util.HashSet;
+import java.util.Iterator;
+
+public class CollectionGroups extends ServiceRack {
+
+	private Element hierarchy = null;
+	private Element groupDesc = null;
+
+	private static final String GROUP_CONTENT = "GroupCurrentContent";
+	private static final String UNIQUE_COLLECTIONS = "UniqueCollections";
+
+	static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.BerryBasket.class.getName());
+
+	@Override
+	protected Element getServiceDescription(Document doc, String service, String lang, String subset) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	String[] _services = { GROUP_CONTENT, UNIQUE_COLLECTIONS };
+
+	public boolean configure(Element info, Element extra_info) {
+		if (!super.configure(info, extra_info)) {
+			return false;
+		}
+
+		logger.info("Configuring CollectionGroups...");
+		this.config_info = info;
+
+		for (int i = 0; i < _services.length; i++) {
+			Element service = this.desc_doc.createElement(GSXML.SERVICE_ELEM);
+			service.setAttribute(GSXML.TYPE_ATT, GSXML.SERVICE_TYPE_GROUPINFO);
+			service.setAttribute(GSXML.NAME_ATT, _services[i]);
+			this.short_service_info.appendChild(service);
+		}
+		// Load group configuration from file
+		readGroupConfiguration();
+
+		return true;
+	}
+
+	protected Element processGroupCurrentContent(Element request) {
+
+		Document doc = XMLConverter.newDOM();
+		UserContext userContext = new UserContext(request);
+		
+		NodeList collList;
+		NodeList groupList;
+		int collIndex = 0;
+		int grpIndex = 0;
+		boolean hierarchy = true;
+		
+	  // Prepare basic response
+		Element result = GSXML.createBasicResponse(doc, GSXML.SERVICE_TYPE_GROUPINFO);
+		Element result_collection_list = doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
+		result.appendChild(result_collection_list);
+		Element result_group_list = doc.createElement(GSXML.GROUP_ELEM + GSXML.LIST_MODIFIER);
+		result.appendChild(result_group_list);
+		
+		// Get param list from request
+		Element paramList = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
+		// Set default group path (main page)
+		String groupPath = "";
+		// If param list not null and group param exists extract value and
+		// override groupPath variable
+		if (paramList != null) {
+			Element paramGroup = GSXML.getNamedElement(paramList, GSXML.PARAM_ELEM, GSXML.NAME_ATT, GSXML.GROUP_ELEM);
+			if (paramGroup != null) {
+				groupPath = paramGroup.getAttribute(GSXML.VALUE_ATT);
+			}
+			Element paramHierarchy = GSXML.getNamedElement(paramList, GSXML.PARAM_ELEM, GSXML.NAME_ATT, GSXML.HIERARCHY_ELEM);
+				if (paramHierarchy != null){
+					hierarchy = true;
+				}
+		}
+		//Remove leading, ending / and dupliclated /
+		groupPath = groupPath.replaceAll("(/+)", "/");
+		groupPath = groupPath.replaceAll("(^/+)|(/+$)", "");
+
+		Element mrCollectionList = getMRCollectionList(userContext);
+		if (mrCollectionList == null){
+			return result;
+		}
+		//Get current groups and collections
+		Element currentContent = getCurrentContent(groupPath);
+		if (currentContent != null){
+			currentContent = (Element) doc.importNode(currentContent, true);
+		}
+	
+		//Get ungrouped collection list
+		Element ungroupedCollectionsList = getUngroupedCollections(mrCollectionList);
+		
+		if (currentContent != null){
+			if (hierarchy){
+				collList = currentContent.getElementsByTagName(GSXML.COLLECTION_ELEM);
+				groupList = currentContent.getElementsByTagName(GSXML.GROUP_ELEM);
+			} else {
+				collList = GSXML.getChildrenByTagName(currentContent, GSXML.COLLECTION_ELEM);
+				groupList = GSXML.getChildrenByTagName(currentContent, GSXML.GROUP_ELEM);
+			}
+			//Check collection list for existence
+			int collListLenght = collList.getLength();
+			for (collIndex = 0; collIndex < collListLenght; collIndex++) {
+				Element currentCollection = (Element) collList.item(collIndex);
+				String collection_name = currentCollection.getAttribute(GSXML.NAME_ATT);
+				Element checkedCollection = GSXML.getNamedElement(mrCollectionList, GSXML.COLLECTION_ELEM,GSXML.NAME_ATT, collection_name);
+				if (checkedCollection != null) {
+					//Set position value
+					if (!hierarchy){
+						int position = GSXML.getNodeIndex(currentCollection); 
+						checkedCollection.setAttribute(GSXML.POSITION_ATT, Integer.toString(position));
+					}
+					// Add collection to response
+					result_collection_list.appendChild(doc.importNode(checkedCollection, true));
+				} else {
+					//remove collection from currentContent
+					currentCollection.getParentNode().removeChild(currentCollection);
+					collIndex--;
+					collListLenght--;
+				}
+			}
+			//check group
+			int groupListLenght = groupList.getLength();
+			for (grpIndex = 0; grpIndex < groupListLenght; grpIndex++) {
+				Element currentGroup = (Element) groupList.item(grpIndex);
+				String currentGroupName = currentGroup.getAttribute(GSXML.NAME_ATT);
+				Element groupDescription = getGroupDescription(currentGroupName);
+				if (groupDescription != null) {
+					//Set position value
+					if (!hierarchy){
+						int position = GSXML.getNodeIndex(currentGroup); 
+						groupDescription.setAttribute(GSXML.POSITION_ATT, Integer.toString(position));
+					}
+					result_group_list.appendChild(doc.importNode(groupDescription, true));
+				} else {
+					//remove group from currentContent
+					currentGroup.getParentNode().removeChild(currentGroup);
+					grpIndex--;
+					groupListLenght--;
+				}
+			}
+			logger.error("Group list lenght " + groupListLenght+ " result_group_list is " + GSXML.elementToString(result_group_list, true));
+			
+		}
+			//Add ungrouped collections if groupPath /+ or "" or null
+			if (groupPath.isEmpty()) {
+				
+				//Add each ungrouped collection to the collection list in loop
+				NodeList ungroupedCollectionNodes = GSXML.getChildrenByTagName(ungroupedCollectionsList, GSXML.COLLECTION_ATT);
+				if (ungroupedCollectionNodes != null) {
+					for (int k = 0; k < ungroupedCollectionNodes.getLength(); k++) {
+						Element ungroupedCollection = (Element) doc.importNode(ungroupedCollectionNodes.item(k), true);
+						if (!hierarchy){
+							ungroupedCollection.setAttribute(GSXML.POSITION_ATT, String.valueOf(grpIndex + collIndex++));
+						}
+						result_collection_list.appendChild(ungroupedCollection);
+						if (hierarchy){
+							
+							currentContent.appendChild(doc.importNode(ungroupedCollectionNodes.item(k), true));
+						}
+					}
+				}
+				
+			} else {
+				Element pathInfo = getPathInfo(groupPath);
+				if (currentContent != null){
+					result.appendChild(doc.importNode(pathInfo, true));
+				}
+			}
+		if (hierarchy){
+			result.appendChild(currentContent);
+		}
+			logger.error("currentContent is " + GSXML.elementToString(currentContent, true));
+		return result;
+	}
+
+	protected Element processUniqueCollections(Element request) {
+		Document doc = XMLConverter.newDOM();
+		UserContext userContext = new UserContext(request);
+		// Prepare basic response
+		Element result = GSXML.createBasicResponse(doc, GSXML.SERVICE_TYPE_GROUPINFO);
+		Element resultCollections = doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
+		result.appendChild(resultCollections);
+		Element mrCollectionList = getMRCollectionList(userContext);
+		//Collections from message router check
+		if (mrCollectionList == null){
+			logger.error("mrCollectionList is null!");
+			return result;
+		}
+		//paramList check
+		Element paramList = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
+		if (paramList == null) {
+			logger.error("UniqueCollections request had no paramList.");
+			return result;
+		}
+		//Add collections from request
+		Set<String> uniq_colls = new HashSet<String>();
+		Element collParam = GSXML.getNamedElement(paramList, GSXML.PARAM_ELEM, GSXML.NAME_ATT, GSXML.COLLECTION_ELEM);
+		if (collParam != null){
+			String colls = GSXML.getValue(collParam);
+			if (!colls.isEmpty())
+			{
+				String[] _colls = colls.split(",");
+				for (int i=0;i < _colls.length;i++){
+					uniq_colls.add(_colls[i]);
+				}
+			}
+		}
+		//groupParam check
+		Element groupParam = GSXML.getNamedElement(paramList, GSXML.PARAM_ELEM, GSXML.NAME_ATT, GSXML.GROUP_ELEM);
+		if (groupParam == null) {
+			logger.error("UniqueCollections request had no groupParam.");
+			return result;
+		}
+		//Add collections from groups
+		String[] groups = null;
+		groups = GSXML.getValue(groupParam).split(",");
+		for (int i = 0; i < groups.length; i++) {
+			Element groupContent = getCurrentContent(groups[i]);
+			//If group exists
+			if (groupContent != null) {
+				NodeList collectionNodes = GSXML.getChildrenByTagName(groupContent, GSXML.COLLECTION_ELEM);
+				for (int j = 0; j < collectionNodes.getLength(); j++) {
+					String collName = ((Element) collectionNodes.item(j)).getAttribute(GSXML.NAME_ATT);
+					uniq_colls.add(collName);
+				}
+			}
+	
+		}
+		//Fill result collectionList 
+		for (Iterator<String> iterator = uniq_colls.iterator(); iterator.hasNext();) {
+			String collectionName = (String) iterator.next();
+			Element checkedCollection = GSXML.getNamedElement(mrCollectionList, GSXML.COLLECTION_ELEM,GSXML.NAME_ATT, collectionName);
+			if (checkedCollection != null){
+				resultCollections.appendChild(doc.importNode(checkedCollection, true));
+			}
+		}
+		return result;
+
+	}
+	
+	private Element getCurrentContent(String path) {
+
+		if (hierarchy == null || groupDesc == null) {
+			return null;
+		}
+		
+		Document doc = XMLConverter.newDOM();
+		
+		if (path == null) {
+			 path = "";
+		}
+		
+		path = path.replaceAll("(/+)", "/");
+		path = path.replaceAll("(^/+)|(/+$)", "");
+		
+		String[] pathSteps = path.split("/");
+
+		
+		Element currentView = (Element) hierarchy.cloneNode(true);
+		// Get the current view
+		for (int i = 0; i < pathSteps.length; i++) {
+			if (!pathSteps[i].isEmpty()) {
+				currentView = GSXML.getNamedElement(currentView, GSXML.GROUP_ELEM, GSXML.NAME_ATT, pathSteps[i]);
+				if (currentView == null){
+					break;
+				}
+			}
+		}
+		if (currentView == null || !currentView.hasChildNodes()) {
+			// Return to the main page
+			return null;
+		}
+		return currentView;
+	}
+
+	private void readGroupConfiguration() {
+
+		File configFile = new File(GSFile.groupConfigFile(site_home));
+		//
+		if (!configFile.exists()) {
+			logger.info("Groups config file " + configFile.getPath() + " does not exist.");
+			return;
+		}
+		// Try to read and catch exception if it fails
+		Document doc = XMLConverter.getDOM(configFile);
+		Element content = doc.getDocumentElement();
+		
+		// XPath to find empty text nodes.
+		try {
+			XPathFactory xpathFactory = XPathFactory.newInstance();
+			XPathExpression xpathExp = xpathFactory.newXPath().compile("//text()[normalize-space(.) = '']");  
+			NodeList emptyTextNodes = (NodeList) xpathExp.evaluate(doc, XPathConstants.NODESET);
+			for (int i = 0; i < emptyTextNodes.getLength(); i++) {
+			    Node emptyTextNode = emptyTextNodes.item(i);
+			    emptyTextNode.getParentNode().removeChild(emptyTextNode);
+			}
+			
+		} catch (XPathExpressionException e) {
+			logger.error("Error occurred while trying to remove emtpy nodes from groupConfig.xml");
+			e.printStackTrace();
+		}  
+		
+		hierarchy = (Element) GSXML.getChildByTagName(content, GSXML.HIERARCHY_ELEM);
+		groupDesc = (Element) GSXML.getChildByTagName(content, GSXML.GROUP_DESC_ELEM);
+	}
+
+	private Element getGroupDescription(String name) {
+		if (groupDesc != null) {
+			Element description = (Element) GSXML.getNamedElement(groupDesc, GSXML.GROUP_ELEM, GSXML.NAME_ATT, name);
+			if (description != null) {
+				return description;
+			}
+			logger.error("GroupDescription is not defined. Check your groupConfig.xml");
+		} 
+			
+		logger.error("No group descriptions found. Check your groupConfig.xml");
+		return null;
+	}
+
+	private Element getUngroupedCollections(Element mr_collection_list) {
+		
+		if (groupDesc == null || hierarchy == null) {
+			logger.error("No group descriptions in groupConfig.xml. Check your groupConfig.xml");
+			//return mr_collection_list
+			return mr_collection_list;
+		}
+		Document doc = XMLConverter.newDOM();
+		// Create Set
+		Set<String> hierarchy_unique_collections = new HashSet<String>();
+		// Element hierarchyCollections = doc.createElement("coll_list");
+		// Get collection nodes
+		
+		NodeList hierarchy_all_collection_list = hierarchy.getElementsByTagName(GSXML.COLLECTION_ELEM);
+		// Save hierarchy collection names to Hashset
+		for (int i = 0; i < hierarchy_all_collection_list.getLength(); i++) {
+			Element collection_element = (Element) hierarchy_all_collection_list.item(i);
+			hierarchy_unique_collections.add(collection_element.getAttribute(GSXML.NAME_ATT));
+		}
+		// Save available by message router collection names to Hashset
+		Set<String> mr_collections = new HashSet<String>();
+		NodeList mr_coll_list = mr_collection_list.getElementsByTagName(GSXML.COLLECTION_ELEM);
+
+		for (int i = 0; i < mr_coll_list.getLength(); i++) {
+			Element collection_element = (Element) mr_coll_list.item(i);
+			mr_collections.add(collection_element.getAttribute(GSXML.NAME_ATT));
+		}
+		//Save collections available by message router and not existed in hierarchy to ungrouped collections set
+		Set<String> ungrouped_collections = new HashSet<String>();
+		for (String string : mr_collections) {
+			if (!hierarchy_unique_collections.contains(string)){
+				ungrouped_collections.add(string);
+			}
+		}
+		//Output
+		Element result = doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
+		for (String name : ungrouped_collections) {
+			Element collection = doc.createElement(GSXML.COLLECTION_ELEM);
+			collection.setAttribute(GSXML.NAME_ATT, name);
+			result.appendChild(collection);
+		}
+		return result;
+
+	}
+	private Element getPathInfo(String path) {
+
+		if (hierarchy == null || groupDesc == null) {
+			return null;
+		}
+		
+		Document doc = XMLConverter.newDOM();
+		
+		if (path == null) {
+			 path = "";
+		}
+		String[] pathSteps = path.split("/");
+
+		Element pathInfo = doc.createElement(GSXML.PATH_ELEM + GSXML.LIST_MODIFIER);
+		
+		String currentPath = "";
+		for (int i = 0; i < pathSteps.length; i++) {
+			if (!pathSteps[i].isEmpty()) {
+				currentPath += "/" + pathSteps[i];
+				Element pathStepDescription = getGroupDescription(pathSteps[i]);
+				if (pathStepDescription != null){
+					pathStepDescription.setAttribute(GSXML.POSITION_ATT, String.valueOf(i));
+					pathStepDescription.setAttribute(GSXML.PATH_ATT, currentPath);
+					pathInfo.appendChild(doc.importNode(pathStepDescription, true));
+				}
+			}
+		}
+		
+		return pathInfo;
+	}
+	private Element getMRCollectionList(UserContext userContext){
+		Document doc = XMLConverter.newDOM();
+		// Get the message router info
+		Element mr_info_message = doc.createElement(GSXML.MESSAGE_ELEM);
+		Element mr_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, "", userContext);
+		mr_info_message.appendChild(mr_request);
+		Element mr_info_response_message = (Element) this.router.process(mr_info_message);
+		if (mr_info_response_message == null) {
+			logger.error(" couldn't query the message router!");
+			return null;
+		}
+		Element mr_info_response = (Element) GSXML.getChildByTagName(mr_info_response_message, GSXML.RESPONSE_ELEM);
+		if (mr_info_response == null) {
+			logger.error("Message router response is null!");
+			return null;
+		}
+		
+		Element mr_collection_list = (Element) GSXML.getChildByTagName(mr_info_response, GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
+		
+		return mr_collection_list;
+	}
+}
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/QueryAction.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/QueryAction.java	(revision 31284)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/action/QueryAction.java	(revision 31285)
@@ -11,4 +11,5 @@
 import org.greenstone.gsdl3.util.GSXSLT;
 import org.greenstone.gsdl3.util.UserContext;
+import org.greenstone.gsdl3.util.XMLConverter;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
@@ -100,4 +101,5 @@
 			if (request_type.indexOf("d") != -1) {
 			  page_response.appendChild(service_description);
+			  addCollectionsHierarchy(page_response,userContext);
 			}
 			//}
@@ -284,4 +286,55 @@
 		addInterfaceOptions(page_response);
 		return page_response;
+	}
+
+	private void addCollectionsHierarchy(Element page_response, UserContext userContext) {
+		Document doc = page_response.getOwnerDocument();
+		String collectionsHierarchy = "CollectionsHierarchy";
+		if (checkServiceAvailable(userContext, collectionsHierarchy)){
+			Element groupQueryMessage = doc.createElement(GSXML.MESSAGE_ELEM);
+			Element groupQueryRequest = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_PROCESS, collectionsHierarchy, userContext);
+			groupQueryMessage.appendChild(groupQueryRequest);
+			Element groupQueryResult = (Element) this.mr.process(groupQueryMessage);
+			if (groupQueryResult == null){
+				return;
+			}
+			Element groupQueryResponse = (Element) GSXML.getChildByTagName(groupQueryResult, GSXML.RESPONSE_ELEM);
+			if (groupQueryResponse == null){
+				return;
+			}
+			Element hierarchy = (Element) GSXML.getChildByTagName(groupQueryResponse, GSXML.HIERARCHY_ELEM);
+			page_response.appendChild(doc.importNode(hierarchy, true));
+		}
+		
+		
+	}
+
+	private boolean checkServiceAvailable(UserContext userContext, String collectionsHierarchy) {
+		
+		Document doc = XMLConverter.newDOM();
+		Element infoMessage = doc.createElement(GSXML.MESSAGE_ELEM);
+		Element infoRequest = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, "", userContext);
+		Element paramList = doc.createElement(GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
+		infoRequest.appendChild(paramList);
+		GSXML.addParameterToList(paramList, GSXML.SUBSET_PARAM, GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER);
+		infoMessage.appendChild(infoRequest);
+		Element responseMessage = (Element) this.mr.process(infoMessage);
+		if (responseMessage == null)
+		{
+			logger.error("couldn't query the message router!");
+			return false;
+		}
+		NodeList serviceLists = responseMessage.getElementsByTagName(GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER);
+		if (serviceLists == null || serviceLists.getLength() == 0){
+			logger.error("No service List in response message from message router!");
+			return false;
+		}
+		Element serviceList = (Element) serviceLists.item(0);
+		Element groupInfoService = GSXML.getNamedElement(serviceList, GSXML.SERVICE_ELEM, GSXML.NAME_ATT, collectionsHierarchy);
+		if (groupInfoService == null){
+			logger.error("service " + collectionsHierarchy + " unavailable");
+			return false;
+		}
+		return true;
 	}
 
@@ -375,3 +428,4 @@
     return true;
   }
+  
 }
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/CollectionGroups.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/CollectionGroups.java	(revision 31284)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/CollectionGroups.java	(revision 31285)
@@ -30,4 +30,5 @@
 	private static final String GROUP_CONTENT = "GroupCurrentContent";
 	private static final String UNIQUE_COLLECTIONS = "UniqueCollections";
+	private static final String COLLECTIONS_HIERARCHY = "CollectionsHierarchy";
 
 	static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.BerryBasket.class.getName());
@@ -39,5 +40,5 @@
 	}
 
-	String[] _services = { GROUP_CONTENT, UNIQUE_COLLECTIONS };
+	String[] _services = { GROUP_CONTENT, UNIQUE_COLLECTIONS, COLLECTIONS_HIERARCHY };
 
 	public boolean configure(Element info, Element extra_info) {
@@ -56,7 +57,5 @@
 		}
 		// Load group configuration from file
-		readGroupConfiguration();
-
-		return true;
+		return readGroupConfiguration();
 	}
 
@@ -86,5 +85,5 @@
 		groupPath = groupPath.replaceAll("(^/+)|(/+$)", "");
 
-		Element mrCollectionList = getMRCollectionList(userContext);
+		Element mrCollectionList = getAvailableCollectionList(userContext);
 		if (mrCollectionList == null){
 			result.appendChild(doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER));
@@ -92,5 +91,5 @@
 		}
 		//Get current groups and collections
-		Element groupContent = getCurrentContent(groupPath);
+		Element groupContent = getRawCurrentContent(groupPath);
 	
 		//Get ungrouped collection list
@@ -99,12 +98,5 @@
 		// If groupContent is empty return empty collection list
 		if (groupContent == null) {
-			// If config file is broken
-			if (hierarchy == null || groupDesc == null) {
-				//Get ungrouped collection list
-				result.appendChild(doc.importNode(ungroupedCollections, true));
-			} else {
-				//If config is ok, but in this group no any collection or group
-				result.appendChild(doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER));
-			}
+			result.appendChild(doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER));
 			return result;
 		}
@@ -139,12 +131,8 @@
 					Element currentGroup = (Element) currentContent.item(i);
 					String currentGroupName = currentGroup.getAttribute(GSXML.NAME_ATT);
-					//Get group description
-					Element group = getGroupDescription(currentGroupName);
-					//Set position value
-					if (group != null) {
-						group.setAttribute(GSXML.POSITION_ATT, String.valueOf(i));
-						result_group_list.appendChild(doc.importNode(group, true));
-					}
-
+					Element groupDescription = getGroupDescription(currentGroupName);
+					
+					groupDescription.setAttribute(GSXML.POSITION_ATT, String.valueOf(i));
+					result_group_list.appendChild(doc.importNode(groupDescription, true));
 				}
 
@@ -152,5 +140,4 @@
 			//Add ungrouped collections if groupPath /+ or "" or null
 			if (groupPath.isEmpty()) {
-				
 				
 				//Add each ungrouped collection to the collection list in loop
@@ -183,5 +170,5 @@
 		Element resultCollections = doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
 		result.appendChild(resultCollections);
-		Element mrCollectionList = getMRCollectionList(userContext);
+		Element mrCollectionList = getAvailableCollectionList(userContext);
 		//Collections from message router check
 		if (mrCollectionList == null){
@@ -218,5 +205,5 @@
 		groups = GSXML.getValue(groupParam).split(",");
 		for (int i = 0; i < groups.length; i++) {
-			Element groupContent = getCurrentContent(groups[i]);
+			Element groupContent = getRawCurrentContent(groups[i]);
 			//If group exists
 			if (groupContent != null) {
@@ -241,16 +228,51 @@
 	}
 	
-	private Element getCurrentContent(String path) {
-
-		if (hierarchy == null || groupDesc == null) {
-			return null;
-		}
-		
-		Document doc = XMLConverter.newDOM();
-		
+	protected Element processCollectionsHierarchy(Element request){
+		
+		Document doc = XMLConverter.newDOM();
+		UserContext userContext = new UserContext(request);
+		// Prepare basic response
+		Element result = GSXML.createBasicResponse(doc, GSXML.SERVICE_TYPE_GROUPINFO);
+		String currentPath = "";
+		Element currentContent = getRawCurrentContent(currentPath);
+		if (currentContent == null){
+			return result;
+		}
+		Element searchableCollectionList = getSearchableCollectionList(userContext);
+		if (searchableCollectionList == null){
+			return result;
+		}
+		
+		//Get ungrouped collection list
+		sanitizeCurrentContent(currentContent, searchableCollectionList);
+		addGroupInfo(currentContent, currentPath);
+		addUngroupedCollections(currentContent, searchableCollectionList);
+		result.appendChild(doc.importNode(currentContent, true));
+		return result;
+	}
+	
+	private void addGroupInfo(Element currentContent, String groupPath) {
+		NodeList groups = GSXML.getChildrenByTagName(currentContent,GSXML.GROUP_ELEM);
+		for (int i=0;i<groups.getLength();i++){
+			Element group = (Element) groups.item(i);
+			String name = group.getAttribute(GSXML.NAME_ATT);
+			String newPath = groupPath + "/" + name; 
+			group.setAttribute(GSXML.PATH_ATT, newPath);
+			Element groupDescription = getGroupDescription(name);
+			Element titleEl = (Element) GSXML.getChildByTagName(groupDescription, GSXML.TITLE_ELEM);
+			String title = titleEl.getTextContent();
+			group.setAttribute(GSXML.TITLE_ELEM, title	);
+			addGroupInfo(group, newPath);
+		}
+		
+	}
+
+	private Element getRawCurrentContent(String path) {
+
 		if (path == null) {
 			 path = "";
 		}
-		
+
+		Document doc = XMLConverter.newDOM();
 		path = path.replaceAll("(/+)", "/");
 		path = path.replaceAll("(^/+)|(/+$)", "");
@@ -259,22 +281,57 @@
 
 		
-		Element currentView = (Element) hierarchy.cloneNode(true);
+		Element currentContent = (Element) hierarchy.cloneNode(true);
 		// Get the current view
 		for (int i = 0; i < pathSteps.length; i++) {
 			if (!pathSteps[i].isEmpty()) {
-				currentView = GSXML.getNamedElement(currentView, GSXML.GROUP_ELEM, GSXML.NAME_ATT, pathSteps[i]);
-				if (currentView == null){
+				currentContent = GSXML.getNamedElement(currentContent, GSXML.GROUP_ELEM, GSXML.NAME_ATT, pathSteps[i]);
+				if (currentContent == null){
 					break;
 				}
 			}
 		}
-		if (currentView == null || !currentView.hasChildNodes()) {
+		if (currentContent == null || !currentContent.hasChildNodes()) {
 			// Return to the main page
 			return null;
 		}
-		return currentView;
-	}
-
-	private void readGroupConfiguration() {
+		return currentContent;
+	}
+	
+	private void sanitizeCurrentContent(Element currentContent, Element checkedCollectionList){
+		if (currentContent == null){
+			return;
+		}
+		NodeList nodes = currentContent.getElementsByTagName(GSXML.COLLECTION_ELEM);
+		for (int i = 0; i < nodes.getLength(); i++) {
+			Element element = (Element) nodes.item(i);
+			String name = element.getAttribute(GSXML.NAME_ATT);
+			Element checkedCollection = GSXML.getNamedElement(checkedCollectionList, GSXML.COLLECTION_ELEM, GSXML.NAME_ATT, name);
+			if (checkedCollection == null) {
+				element.getParentNode().removeChild(element);
+				i--;
+			}
+		}
+	}
+	
+	private void addUngroupedCollections(Element currentContent, Element availableCollections){
+		if (currentContent == null){
+			return;
+		}
+		Document doc = currentContent.getOwnerDocument();
+		NodeList collectionList = availableCollections.getElementsByTagName(GSXML.COLLECTION_ELEM);
+		for (int i = 0; i < collectionList.getLength();i++){
+			Element collection = (Element) collectionList.item(i);
+			String name = collection.getAttribute(GSXML.NAME_ATT);
+			NodeList foundCollection = GSXML.getNamedElements(currentContent, GSXML.COLLECTION_ELEM, GSXML.NAME_ATT, name);
+			if (foundCollection.getLength() == 0){
+				Element ungroupedCollection = doc.createElement(GSXML.COLLECTION_ELEM);
+				ungroupedCollection.setAttribute(GSXML.NAME_ATT, name);
+				currentContent.appendChild(ungroupedCollection);
+			}
+		}
+		
+	}
+
+	private boolean readGroupConfiguration() {
 
 		File configFile = new File(GSFile.groupConfigFile(site_home));
@@ -282,5 +339,5 @@
 		if (!configFile.exists()) {
 			logger.info("Groups config file " + configFile.getPath() + " does not exist.");
-			return;
+			return false;
 		}
 		// Try to read and catch exception if it fails
@@ -301,30 +358,46 @@
 			logger.error("Error occurred while trying to remove emtpy nodes from groupConfig.xml");
 			e.printStackTrace();
+			return false;
 		}  
 		
 		hierarchy = (Element) GSXML.getChildByTagName(content, GSXML.HIERARCHY_ELEM);
 		groupDesc = (Element) GSXML.getChildByTagName(content, GSXML.GROUP_DESC_ELEM);
+		if (hierarchy == null || groupDesc == null){
+			logger.error("Error processing groups configuration. Check groupConfig.xml");
+			return false;
+		}
+		verifyGroupDescription();
+		return true;
+	}
+
+	private void verifyGroupDescription() {
+		Document doc = groupDesc.getOwnerDocument();
+		NodeList groups = hierarchy.getElementsByTagName(GSXML.GROUP_ELEM);
+		for (int i = 0; i < groups.getLength(); i++) {
+			Element group = (Element) groups.item(i); 
+			String name = group.getAttribute(GSXML.NAME_ATT);
+			Element foundDescription = GSXML.getNamedElement(groupDesc, GSXML.GROUP_ELEM, GSXML.NAME_ATT, name);
+			if (foundDescription == null){
+				Element defaultDescription = doc.createElement(GSXML.GROUP_ELEM); 
+				defaultDescription.setAttribute(GSXML.NAME_ATT, name);
+				Element groupTitle = doc.createElement(GSXML.TITLE_ELEM);
+				groupTitle.setTextContent(name);
+				defaultDescription.appendChild(groupTitle);
+				groupDesc.appendChild(defaultDescription);
+			}
+		} 
+		
 	}
 
 	private Element getGroupDescription(String name) {
-		if (groupDesc != null) {
-			Element description = (Element) GSXML.getNamedElement(groupDesc, GSXML.GROUP_ELEM, GSXML.NAME_ATT, name);
-			if (description != null) {
-				return description;
-			}
+		Element description = (Element) GSXML.getNamedElement(groupDesc, GSXML.GROUP_ELEM, GSXML.NAME_ATT, name);
+		if (description == null) {
 			logger.error("GroupDescription is not defined. Check your groupConfig.xml");
-		} 
-			
-		logger.error("No group descriptions found. Check your groupConfig.xml");
-		return null;
+		}
+		return description;
 	}
 
 	private Element getUngroupedCollections(Element mr_collection_list) {
 		
-		if (groupDesc == null || hierarchy == null) {
-			logger.error("No group descriptions in groupConfig.xml. Check your groupConfig.xml");
-			//return mr_collection_list
-			return mr_collection_list;
-		}
 		Document doc = XMLConverter.newDOM();
 		// Create Set
@@ -366,8 +439,4 @@
 	private Element getPathInfo(String path) {
 
-		if (hierarchy == null || groupDesc == null) {
-			return null;
-		}
-		
 		Document doc = XMLConverter.newDOM();
 		
@@ -387,6 +456,6 @@
 					pathStepDescription.setAttribute(GSXML.POSITION_ATT, String.valueOf(i));
 					pathStepDescription.setAttribute(GSXML.PATH_ATT, currentPath);
-					pathInfo.appendChild(doc.importNode(pathStepDescription, true));
-				}
+				}
+				pathInfo.appendChild(doc.importNode(pathStepDescription, true));
 			}
 		}
@@ -394,5 +463,5 @@
 		return pathInfo;
 	}
-	private Element getMRCollectionList(UserContext userContext){
+	private Element getAvailableCollectionList(UserContext userContext){
 		Document doc = XMLConverter.newDOM();
 		// Get the message router info
@@ -415,3 +484,28 @@
 		return mr_collection_list;
 	}
+	private Element getSearchableCollectionList(UserContext userContext){
+		Document doc = XMLConverter.newDOM();
+		Element collectionList = doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
+		// Get the message router info
+		Element mr_info_message = doc.createElement(GSXML.MESSAGE_ELEM);
+		Element mr_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, "TextQuery" , userContext);
+		mr_info_message.appendChild(mr_request);
+		Element mr_info_response_message = (Element) this.router.process(mr_info_message);
+		if (mr_info_response_message == null) {
+			logger.error(" couldn't query the message router!");
+			return null;
+		}
+		NodeList options = mr_info_response_message.getElementsByTagName(GSXML.PARAM_OPTION_ELEM);
+		for (int i = 0; i < options.getLength(); i++) {
+			Element option = (Element) options.item(i);
+			String name = option.getAttribute(GSXML.NAME_ATT);
+			if (name.equals("all")){
+				continue;
+			}
+			Element collection = doc.createElement(GSXML.COLLECTION_ELEM);
+			collection.setAttribute(GSXML.NAME_ATT, name);
+			collectionList.appendChild(collection);
+		}
+		return collectionList;
+	}
 }
Index: /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/CrossCollectionSearch.java
===================================================================
--- /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/CrossCollectionSearch.java	(revision 31284)
+++ /main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/CrossCollectionSearch.java	(revision 31285)
@@ -533,6 +533,6 @@
 			groupParamList.appendChild(doc.importNode(GSXML.getNamedElement(paramList, GSXML.PARAM_ELEM, GSXML.NAME_ATT, GSXML.GROUP_ELEM), true));
 			Element groupQueryResult = (Element) this.router.process(groupQueryMessage);
-			Element groupQueryResonse = (Element) GSXML.getChildByTagName(groupQueryResult, GSXML.RESPONSE_ELEM);
-			Element collList = (Element) GSXML.getChildByTagName(groupQueryResonse, GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
+			Element groupQueryResponse = (Element) GSXML.getChildByTagName(groupQueryResult, GSXML.RESPONSE_ELEM);
+			Element collList = (Element) GSXML.getChildByTagName(groupQueryResponse, GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
 			NodeList collections = GSXML.getChildrenByTagName(collList, GSXML.COLLECTION_ELEM);
 			collArray = new String[collections.getLength()];
