/****************************************************************************
 *  This software is part of the gCube Project.
 *  Site: http://www.gcube-system.org/
 ****************************************************************************
 * The gCube/gCore software is licensed as Free Open Source software
 * conveying to the EUPL (http://ec.europa.eu/idabc/eupl).
 * The software and documentation is provided by its authors/distributors
 * "as is" and no expressed or
 * implied warranty is given for its use, quality or fitness for a
 * particular case.
 ****************************************************************************
 * Filename: ISClientRequester.java
 ****************************************************************************
 * @author <a href="mailto:daniele.strollo@isti.cnr.it">Daniele Strollo</a>
 ***************************************************************************/

package org.gcube.resourcemanagement.support.server.gcube;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;

import javax.xml.transform.OutputKeys;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.gcube.common.core.contexts.GHNContext;
import org.gcube.common.core.informationsystem.client.ISClient;
import org.gcube.common.core.informationsystem.client.QueryParameter;
import org.gcube.common.core.informationsystem.client.XMLResult;
import org.gcube.common.core.informationsystem.client.XMLResult.ISResultEvaluationException;
import org.gcube.common.core.informationsystem.client.queries.GCUBEGenericQuery;
import org.gcube.common.core.scope.GCUBEScope;
import org.gcube.common.core.utils.logging.GCUBEClientLog;
import org.gcube.resourcemanagement.support.client.views.ResourceTypeDecorator;
import org.gcube.resourcemanagement.support.server.gcube.queries.QueryLoader;
import org.gcube.resourcemanagement.support.server.gcube.queries.QueryLocation;


import org.gcube.resourcemanagement.support.server.managers.scope.ScopeManager;
import org.gcube.resourcemanagement.support.server.utils.ServerConsole;
import org.gcube.resourcemanagement.support.shared.plugins.GenericResourcePlugin;
import org.gcube.resourcemanagement.support.shared.plugins.TMPluginFormField;
import org.gcube.resourcemanagement.support.shared.types.Tuple;
import org.gcube.resourcemanagement.support.shared.types.datamodel.CompleteResourceProfile;
import org.gcube.resourcemanagement.support.shared.types.datamodel.ResourceDescriptor;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;


/**
 * All the requests to the IS are implemented here.
 * @author Massimiliano Assante (ISTI-CNR)
 * @author Daniele Strollo 
 */
public class ISClientRequester {
	static GCUBEClientLog _log = new GCUBEClientLog(ISClientRequester.class);

	private static final ISQueryCache CACHE = new ISQueryCache();
	private static final String LOG_PREFIX = "[ISCLIENT-REQS]";

	public static void emptyCache() {
		CACHE.empty();
	}

	private static final ArrayList<String> getResourceTypes(
			final CacheManager status,
			final GCUBEScope queryScope)
					throws Exception {
		List<XMLResult> results = null;
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);
		isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_TREE_TYPES));

		// Handles the cache
		ISQueryCacheKeyT cacheKey = new ISQueryCacheKeyT(queryScope.toString(),
				isQuery.getExpression(),
				"getResourcesTypes");

		if (status.isUsingCache() && CACHE.contains(cacheKey) && CACHE.get(cacheKey) != null) {
			results = CACHE.get(cacheKey);
		} else {
			try {
				results = client.execute(isQuery, queryScope);
				if (status.isUsingCache()) {
					CACHE.insert(cacheKey, results);
				}
			} catch (Exception e) {
				throw new Exception(e.getMessage());
			}
		}
		if (results == null || results.size() == 0) {
			return null;
		}

		String type = null;
		ArrayList<String> types = new ArrayList<String>();
		try {			
			for (XMLResult elem : results) {

				type = elem.evaluate("/type/text()").get(0);
				if (type != null && type.trim().length() > 0) {
					types.add(type.trim());
				}
			}
			//****  	CHANGES TO KNOW IF VIEWS AND GCUBECollection are present
			types.remove("MetadataCollection");

			isQuery.setExpression("declare namespace vm = 'http://gcube-system.org/namespaces/contentmanagement/viewmanager';"+
					"declare namespace gc = 'http://gcube-system.org/namespaces/common/core/porttypes/GCUBEProvider';"+
					"declare namespace is = 'http://gcube-system.org/namespaces/informationsystem/registry';" +
					"for $data in collection(\"/db/Properties\")//Document, $view in $data/Data where $view/gc:ServiceName/string() eq \"ViewManager\" " +
					" and $view/gc:ServiceClass/string() eq \"ContentManagement\" and count($view//vm:View)>0 return <a>true</a>");

			if (client.execute(isQuery, queryScope).size() > 0)
				types.add("VIEW");

			isQuery.setExpression("declare namespace gc = 'http://gcube-system.org/namespaces/common/core/porttypes/GCUBEProvider';"+
					"declare namespace is = 'http://gcube-system.org/namespaces/informationsystem/registry';" +
					"for $data in collection(\"/db/Profiles/GenericResource\")//Document/Data/is:Profile/Resource where $data/Profile/SecondaryType eq \"GCUBECollection\" " +
					" return <a>true</a>");

			if (client.execute(isQuery, queryScope).size() > 0)
				types.add("Collection");

		} catch (ISResultEvaluationException e) {
			_log.error("error during getResourcesTypes");
		} catch (IndexOutOfBoundsException e) {
			// ignore exception
		}

		return types;
	}

	private static final ArrayList<String> getResourceSubTypes(
			final CacheManager status,
			final GCUBEScope queryScope,
			final String resourceType)
					throws Exception {

		ArrayList<String> subtypes = new ArrayList<String>();
		if (resourceType.equals("Collection")) {
			subtypes.add("System");
			subtypes.add("User");	
			return subtypes;
		}
		if (resourceType.equals("VIEW")) {
			subtypes.add("Not supported");
			return subtypes;
		}		

		List<XMLResult> results = null;
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);
		isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_TREE_SUBTYPES));

		isQuery.addParameters(new QueryParameter("RES_TYPE", resourceType));

		// Handles the cache
		ISQueryCacheKeyT cacheKey = new ISQueryCacheKeyT(queryScope.toString(),
				isQuery.getExpression(),
				"getResourcesSubTypes");

		if (status.isUsingCache() && CACHE.contains(cacheKey) && CACHE.get(cacheKey) != null) {
			results = CACHE.get(cacheKey);
		} else {
			try {
				results = client.execute(isQuery, queryScope);
				if (status.isUsingCache()) {
					CACHE.insert(cacheKey, results);
				}
			} catch (Exception e) {
				throw new Exception(e.getMessage());
			}
		}
		if (results == null || results.size() == 0) {
			return null;
		}

		String subtype = null;

		for (XMLResult elem : results) {
			try {
				subtype = elem.evaluate("/subtype/text()").get(0);
				if (subtype != null && subtype.trim().length() > 0) {
					subtypes.add(subtype.trim());
				}
			} catch (ISResultEvaluationException e) {
				_log.error("error during getResourcesTypes");
			} catch (IndexOutOfBoundsException e) {
				// ignore exception
			}
		}
		return subtypes;
	}

	/**
	 * For all the resource in the scope retrieves their
	 * (type, subtype) values.
	 * The result is a list of couples of that form.
	 * @return a list of string tuples (type, subtype)
	 * @throws Exception
	 */
	public static final HashMap<String, ArrayList<String>> getResourcesTree(  //qui si perde
			final CacheManager status,
			final GCUBEScope queryScope)
					throws Exception {
		HashMap<String, ArrayList<String>> retval = new HashMap<String, ArrayList<String>>();

		// Loads the Resources
		ArrayList<String> types = getResourceTypes(status, queryScope);
		ArrayList<String> subtypes = null;

		for (String type : types) {
			try {
				subtypes = getResourceSubTypes(status, queryScope, type);
				if (subtypes != null && subtypes.size() > 0) {
					retval.put(type, subtypes);
				}
			} catch (Exception e) {
				_log.error(LOG_PREFIX, e);
			}
		}

		// Loads the WSResources
		// This types is statically handled since it is a particular
		// case of resource.
		List<XMLResult> results = null;
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);
		isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_WSRES_TYPES));
		results = client.execute(isQuery, queryScope);
		if (results == null || results.size() == 0) {
			return retval;
		}
		subtypes = new ArrayList<String>();
		for (XMLResult elem : results) {
			subtypes.add(elem.toString());
		}
		retval.put("WSResource", subtypes);
		return retval;
	}

	public static final List<String> getRelatedResources(
			final CacheManager status,
			final String type,
			final String id,
			final GCUBEScope queryScope)
					throws Exception {
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);

		QueryLocation queryPath = null;
		try {
			queryPath = QueryLocation.valueOf("LIST_RELATED_" + type);
		} catch (Exception e) {
			ServerConsole.error(LOG_PREFIX, "Getting the resource query.", e);
			throw new Exception(e.getMessage());
		}
		isQuery.setExpression(QueryLoader.getQuery(queryPath));
		isQuery.addParameters(new QueryParameter("RES_ID", id.trim()));

		ISQueryCacheKeyT cacheKey = new ISQueryCacheKeyT(queryScope.toString(),
				isQuery.getExpression(),
				"getResourceRelated");

		List<XMLResult> results = null;
		// Handle cache
		if (status.isUsingCache() && CACHE.contains(cacheKey) && CACHE.get(cacheKey) != null) {
			results = CACHE.get(cacheKey);
		} else {
			results = client.execute(isQuery, queryScope);
			if (status.isUsingCache()) {
				CACHE.insert(cacheKey, results);
			}
		}
		if (results == null || results.size() == 0) {
			return null;
		}
		// ENDOF Handle cache

		List<String> retval = new Vector<String>();

		for (XMLResult elem : results) {
			// Removes the resources with no ID or empty
			try {
				String toAdd = elem.toString();
				if (toAdd != null) {
					toAdd = toAdd.replace("<Resources>", "");
					toAdd = toAdd.replace("</Resources>", "");
				}
				retval.add(toAdd);
			} catch (Exception e) {
				ServerConsole.debug(LOG_PREFIX, "[getResourcesRelated] found and invalid resource");
			}
		}
		//ServerConsole.trace(LOG_PREFIX, "Retrieved (" + retval.size() + ") ServiceDetails for type: " + type);
		return retval;
	}

	public static final List<String> getResourcesByType(
			final CacheManager status,
			final GCUBEScope queryScope,
			final String type,
			final String subType)
					throws Exception {
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);

		QueryLocation queryPath = null;
		try {
			if (type.equals(ResourceTypeDecorator.WSResource.name())) {
				queryPath = QueryLocation.GET_WSRES_DETAILS_BYSUBTYPE;
			} else {
				queryPath = QueryLocation.valueOf("LIST_" + type);
			}
		} catch (Exception e) {
			ServerConsole.error(LOG_PREFIX, "Getting the resource query.", e);
			throw new Exception(e.getMessage());
		}

		isQuery.setExpression(QueryLoader.getQuery(queryPath));
		if (type.equals(ResourceTypeDecorator.WSResource.name())) {
			if (subType != null && subType.length() > 0) {
				isQuery.addParameters(new QueryParameter("RES_SUBTYPE",
						subType.trim()));
			}
		} else {
			if (subType != null && subType.length() > 0) {
				if (subType.equalsIgnoreCase("User") || subType.equalsIgnoreCase("System")) {
					isQuery.addParameters(new QueryParameter("RES_SUBTYPE", " and $profiles/Profile/Body/CollectionInfo/user/text() = \"" + ((subType.trim().equals("User")) ? "true" : "false") + "\""));
				}
				else 
					isQuery.addParameters(new QueryParameter("RES_SUBTYPE", "where $subtype = \"" + subType.trim() + "\""));
			}
		}

		ISQueryCacheKeyT cacheKey = new ISQueryCacheKeyT(queryScope.toString(),
				isQuery.getExpression(),
				"getResourcesTypes");

		List<XMLResult> results = null;

		if (status.isUsingCache() && CACHE.contains(cacheKey) && CACHE.get(cacheKey) != null) {
			results = CACHE.get(cacheKey);
		} else {
			results = client.execute(isQuery, queryScope);
			if (status.isUsingCache()) {
				CACHE.insert(cacheKey, results);
			}
		}
		if (results == null || results.size() == 0) {
			return null;
		}
		List<String> retval = new Vector<String>();

		for (XMLResult elem : results) {
			// Removes the resources with no ID or empty
			try {
				if (elem.evaluate("//ID").get(0) != null && elem.evaluate("//ID").get(0).trim().length() > 0) {
					retval.add(elem.toString());
					//ServerConsole.debug("", elem.toString());// Print the result 
				} else {
					ServerConsole.debug(LOG_PREFIX, "*** Found an invalid element with no ID");
				}

			} catch (Exception e) {
				ServerConsole.debug(LOG_PREFIX, "[getResourcesByType] found a resource with empty ID");
			}
		}
		//ServerConsole.trace(LOG_PREFIX, "Retrieved (" + retval.size() + ") ServiceDetails for type: " + type);
		return retval;
	}

	public static final List<ResourceDescriptor> getResourceModels(
			final GCUBEScope queryScope,
			final String type,
			final String subType,
			final List<Tuple<String>> additionalMaps)
					throws Exception {
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);
		if (subType != null && subType.trim().length() > 0) {
			isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_RES_DETAILS_BYSUBTYPE));
			isQuery.addParameters(
					new QueryParameter("RES_TYPE", type)
					);
			isQuery.addParameters(
					new QueryParameter("RES_SUBTYPE", subType)
					);
		} else {
			isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_RES_DETAILS_BYTYPE));
			isQuery.addParameters(
					new QueryParameter("RES_TYPE", type)
					);
		}
		List<XMLResult> results = client.execute(isQuery, queryScope);

		List<ResourceDescriptor> retval = new Vector<ResourceDescriptor>();
		ResourceDescriptor toAdd = null;
		for (XMLResult elem : results) {
			// Removes the resources with no ID or empty
			try {
				if (elem.evaluate("//ID").get(0) != null) {
					toAdd = new ResourceDescriptor(
							elem.evaluate("//Type/text()").get(0),
							elem.evaluate("//SubType/text()").get(0),
							elem.evaluate("//ID/text()").get(0),
							elem.evaluate("//Name/text()").get(0));

					// Additional mappings can be defined by the requester.
					// e.g. new Tuple("description", "//Profile/Description/text()");
					if (additionalMaps != null && additionalMaps.size() > 0) {
						for (Tuple<String> map : additionalMaps) {
							try {
								toAdd.addProperty(map.get(0),
										elem.evaluate(map.get(1)).get(0));
							} catch (final Exception e) {
								toAdd.addProperty(map.get(0),
										"");
							}
						}
					}

					retval.add(toAdd);
				}
			} catch (Exception e) {
				ServerConsole.debug(LOG_PREFIX, "[getResourcesByType] found a resource with empty ID");
			}
		}
		//ServerConsole.trace(LOG_PREFIX, "Retrieved (" + retval.size() + ") ServiceDetails for type: " + type);
		return retval;
	}


	public static final List<String> getWSResources(
			final GCUBEScope queryScope)
					throws Exception {
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);
		isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_WSRES_DETAILS_BYTYPE));
		List<XMLResult> results = client.execute(isQuery, queryScope);

		List<String> retval = new Vector<String>();

		for (XMLResult elem : results) {
			retval.add(elem.toString());
		}
		//ServerConsole.trace(LOG_PREFIX, "Retrieved (" + retval.size() + ") ServiceDetails for type: " + ResourceTypeDecorator.WSResource.name());
		return retval;
	}

	public static final CompleteResourceProfile getResourceByID(
			final String xmlFilePath,
			final GCUBEScope queryScope,
			final String resType,
			final String resID)
					throws Exception {
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);
		if (resType == null) {
			isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_RESOURCE_BYID));
			isQuery.addParameters(
					new QueryParameter("RES_ID", resID)
					);
		} else if (resType.equalsIgnoreCase(ResourceTypeDecorator.WSResource.name())) {
			isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_WSRESOURCE_BYID));
			isQuery.addParameters(
					new QueryParameter("RES_ID", resID)
					);
		} else {
			isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_RESOURCE_BYID));
			isQuery.addParameters(
					new QueryParameter("RES_ID", resID),
					new QueryParameter("RES_TYPE", resType)
					);
		}
		List<XMLResult> results = client.execute(isQuery, queryScope);


		//ServerConsole.trace(LOG_PREFIX, "Retrieved (" + retval.size() + ") Resource for ID: " + resID);

		if (results != null && results.size() > 0) {
			String type = null;
			if (resType == null) {
				try {
					type = results.get(0).evaluate("/Resource/Type/text()").get(0);
				} catch (Exception e) {
					ServerConsole.error(LOG_PREFIX, e);
				}
			} else {
				type = resType;
			}
			String xmlRepresentation = results.get(0).toString();
			String htmlRepresentation = XML2HTML(xmlRepresentation, xmlFilePath);
			return new CompleteResourceProfile(resID, ResourceTypeDecorator.valueOf(type), getResourceName(type, resID, results.get(0)), xmlRepresentation, htmlRepresentation);
		}
		return null;
	}

	public static Map<String, GenericResourcePlugin> getGenericResourcePlugins(final GCUBEScope scope) throws Exception {
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);
		isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_GENERIC_RESOURCE_PLUGINS));
		List<XMLResult> results = client.execute(isQuery, scope);

		Map<String, GenericResourcePlugin> retval = new HashMap<String, GenericResourcePlugin>();
		gonext: for (XMLResult plugin : results) {

			try {
				for (String entry : plugin.evaluate("/CMPlugins/Plugin/Entry")) {

					Document doc = ScopeManager.getDocumentGivenXML(entry);
					String name = doc.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
					_log.trace("[LOAD-PLUGIN] found: *** name " + name);
					String pluginType = doc.getElementsByTagName("Type").item(0).getFirstChild().getNodeValue();
					_log.trace("[LOAD-PLUGIN] found: *** type " + pluginType);
					String description = doc.getElementsByTagName("description").item(0).getFirstChild().getNodeValue();
					_log.trace("[LOAD-PLUGIN] found: *** description " + description);
					String namespace = null;
					try {
						namespace =  doc.getElementsByTagName("namespace").item(0).getFirstChild().getNodeValue();
						_log.debug("[LOAD-PLUGIN] found: *** namespace " + namespace);
					} catch (Exception e) {
						_log.warn("[LOAD-PLUGIN] namespace not found");
					}

					GenericResourcePlugin toAdd = new GenericResourcePlugin(name, namespace, description, pluginType);

					NodeList params = doc.getElementsByTagName("param");
					for (int i = 0; i < params.getLength(); i++) {

						NodeList paramTree = params.item(i).getChildNodes();
						String paramName = null;
						String paramDefinition = null;
						for (int j = 0; j < paramTree.getLength(); j++) {
							if (paramTree.item(j).getNodeName().equals("param-name")) {
								paramName = paramTree.item(j).getFirstChild().getNodeValue();
							}

							if (paramTree.item(j).getNodeName().equals("param-definition")) {
								paramDefinition = paramTree.item(j).getFirstChild().getNodeValue();
							}
						}

						_log.trace("[LOAD-PLUGIN] found: param " + paramName);

						GenericResourcePlugin.Field paramField = new GenericResourcePlugin.Field(paramName, GenericResourcePlugin.FieldType.string);
						if (paramDefinition != null) {
							StringTokenizer parser = new StringTokenizer(paramDefinition, ";");
							while (parser.hasMoreTokens()) {
								try {
									String currElem = parser.nextToken();
									String key = currElem.substring(0, currElem.indexOf("="));
									String value = currElem.substring(currElem.indexOf("=") + 1, currElem.length()).trim();
									if (key.equals("type")) {
										paramField.setType(GenericResourcePlugin.FieldType.valueOf(value));
									}
									if (key.equals("opt")) {
										paramField.setIsRequired(!Boolean.parseBoolean(value));
									}
									if (key.equals("label")) {
										if (value.startsWith("'")) {
											value = value.substring(1, value.length());
										}
										if (value.endsWith("'")) {
											value = value.substring(0, value.length() - 1);
										}
										paramField.setLabel(value);
									}
									if (key.equals("default")) {
										if (value.startsWith("'")) {
											value = value.substring(1, value.length());
										}
										if (value.endsWith("'")) {
											value = value.substring(0, value.length() - 1);
										}
										paramField.setDefaultValue(value);
									}
								} catch (Exception e) {
									// parsing error - not well formed string
								}
							}
						}
						toAdd.addParam(paramField);
					}

					retval.put(name + "::" + pluginType, toAdd);
				}
			} catch (RuntimeException e) {
				continue gonext;
			}


		}

		return retval;
	}
	/**
	 * get the plugins for tree manager
	 * @param scope
	 * @return a map containing the plugin name as key and a List of formfield
	 * @throws Exception
	 */
	public static HashMap<String, ArrayList<TMPluginFormField>> getGenericResourceTreeManagerPlugins(final GCUBEScope scope) throws Exception {
		ISClient client = GHNContext.getImplementation(ISClient.class);
		GCUBEGenericQuery isQuery = null;
		isQuery = client.getQuery(GCUBEGenericQuery.class);
		isQuery.setExpression(QueryLoader.getQuery(QueryLocation.GET_GENERIC_RESOURCE_TREE_MANAGER_PLUGINS));
		List<XMLResult> results = client.execute(isQuery, scope);

		HashMap<String, ArrayList<TMPluginFormField>> retval = new HashMap<String, ArrayList<TMPluginFormField>>();
		gonext: for (XMLResult plugin : results) {
			try {				
				for (String entry : plugin.evaluate("/TMPlugins/Plugin/Entry")) {
					String requestName = null;
					boolean foundRequest = false;
					Document doc = ScopeManager.getDocumentGivenXML(entry);
					String name = doc.getElementsByTagName("name").item(0).getFirstChild().getNodeValue();
					_log.trace("[LOAD-PLUGIN] found: *** name " + name);
					String pluginType = doc.getElementsByTagName("Type").item(0).getFirstChild().getNodeValue();
					_log.trace("[LOAD-PLUGIN] found: *** type " + pluginType);
					String description = doc.getElementsByTagName("description").item(0).getFirstChild().getNodeValue();
					_log.trace("[LOAD-PLUGIN] found: *** description " + description);
					String namespace = null;
					try {
						namespace =  doc.getElementsByTagName("namespace").item(0).getFirstChild().getNodeValue();
						_log.trace("[LOAD-PLUGIN] found: *** namespace " + namespace);
					} catch (Exception e) {
						_log.warn("[LOAD-PLUGIN] namespace not found");
					}

					NodeList params = doc.getElementsByTagName("param");
					ArrayList<TMPluginFormField> formFields = new ArrayList<TMPluginFormField>();
					
					for (int i = 0; i < params.getLength(); i++) {

						NodeList paramTree = params.item(i).getChildNodes();
						String paramName = null;
						String xmlToParse = null;
						boolean foundSample = false;
						for (int j = 0; j < paramTree.getLength(); j++) {							
							if (paramTree.item(j).getNodeName().equals("param-name")) {
								paramName = paramTree.item(j).getFirstChild().getNodeValue();

								if (paramName.compareTo("requestSample") == 0) {
									foundSample = true;
									foundRequest = true;
								}							
							}
							if (paramTree.item(j).getNodeName().equals("param-definition") && foundSample) {
								xmlToParse = paramTree.item(j).getFirstChild().getNodeValue();
								xmlToParse = xmlToParse.replaceAll("&lt;", "<");
								xmlToParse = xmlToParse.replaceAll("&gt;", "<");
								foundSample = false;		

								requestName = getRequestName(xmlToParse);

								formFields = getPluginFormFromXml(xmlToParse);
							}
						}
//						if (params.getLength()>1)
						retval.put(name + ":" + requestName, formFields);						
							
					}
					if (foundRequest == false){				
					retval.put(name, formFields);
					}
					
				}
			} catch (RuntimeException e) {
				continue gonext;
			}
		}
		return retval;
	}


	/**
	 * get Request Name
	 * 
	 * 
	 * @param xmlToParse
	 * @return the list 
	 */
	private static String getRequestName(String xmlToParse) {
		Document doc = ScopeManager.getDocumentGivenXML(xmlToParse);

		return doc.getDocumentElement().getNodeName();

	}

	/**
	 * parses the following and return the list to generate the form automatically
	 * 
	 * sample
	 * <speciesRequest>
	 * 	<name>Parachela collection</name>
	 * 	<description>Parachela collection from Itis</description>
	 * 	<scientificNames repeatable="true">Parachela</scientificNames>
	 * 	<datasources repeatable="true">ITIS</datasources>
	 * 	<strictMatch>true</strictMatch>
	 * 	<refreshPeriod>5</refreshPeriod>
	 * 	<timeUnit>MINUTES</timeUnit>
	 * </speciesRequest>
	 * 
	 * @param xmlToParse
	 * @return the list 
	 */
	private static ArrayList<TMPluginFormField> getPluginFormFromXml(String xmlToParse) {
		ArrayList<TMPluginFormField> toReturn = new ArrayList<TMPluginFormField>();
		Document doc = ScopeManager.getDocumentGivenXML(xmlToParse);
		//		Node root = doc.getElementsByTagName("request").item(0);

		Node root = doc.getDocumentElement();
		NodeList children = root.getChildNodes();
		for (int i = 0; i < children.getLength(); i++) {
			String label = children.item(i).getNodeName();
			String defaultValue =  children.item(i).getFirstChild().getNodeValue();
			boolean repeatable = false;
			boolean required = false;
			if (children.item(i).hasAttributes()) {
				NamedNodeMap attributes = children.item(i).getAttributes();
				if (children.item(i).getAttributes().getNamedItem("repeatable") != null)
					repeatable = attributes.getNamedItem("repeatable").getNodeValue().equalsIgnoreCase("true");	
				if (children.item(i).getAttributes().getNamedItem("required") != null)
					required = attributes.getNamedItem("required").getNodeValue().equalsIgnoreCase("true");	
			}
			toReturn.add(new TMPluginFormField(label, defaultValue, required, repeatable));
		}
		return toReturn;
	}

	/**
	 * From the ID of a resource retrieves its name. Notice that resource name
	 * is retrieved according to their type.
	 * @param type the type of the resource
	 * @param ID the identifier of the resource
	 * @param node the XML node from which retrieve the information
	 * @return
	 */
	private static String getResourceName(String type, String ID, XMLResult node) {
		if (type.equalsIgnoreCase(ResourceTypeDecorator.GHN.name())) {
			try {
				return node.evaluate("/Resource/Profile/GHNDescription/Name/text()").get(0);
			} catch (Exception e) {
				return ID;
			}
		}
		if (type.equalsIgnoreCase(ResourceTypeDecorator.Collection.name())) {
			try {
				return node.evaluate("/Resource/Profile/Name/text()").get(0);
			} catch (Exception e) {
				return ID;
			}
		}
		if (type.equalsIgnoreCase(ResourceTypeDecorator.Service.name())) {
			try {
				return node.evaluate("/Resource/Profile/Name/text()").get(0);
			} catch (Exception e) {
				return ID;
			}
		}
		if (type.equalsIgnoreCase(ResourceTypeDecorator.RunningInstance.name())) {
			try {
				return node.evaluate("/Resource/Profile/ServiceName/text()").get(0);
			} catch (Exception e) {
				return ID;
			}
		}
		if (type.equalsIgnoreCase(ResourceTypeDecorator.VIEW.name())) {
			try {
				return node.evaluate("/Resource/Profile/Name/text()").get(0);
			} catch (Exception e) {
				return ID;
			}
		}
		if (type.equalsIgnoreCase(ResourceTypeDecorator.RuntimeResource.name())) {
			try {
				return node.evaluate("/Resource/Profile/Name/text()").get(0);
			} catch (Exception e) {
				return ID;
			}
		}
		if (type.equalsIgnoreCase(ResourceTypeDecorator.GenericResource.name())) {
			try {
				return node.evaluate("/Resource/Profile/Name/text()").get(0);
			} catch (Exception e) {
				return ID;
			}
		}
		if (type.equalsIgnoreCase(ResourceTypeDecorator.WSResource.name())) {
			try {
				return node.evaluate("/Document/Data/child::*[local-name()='ServiceName']/text()").get(0);
			} catch (Exception e) {
				return ID;
			}
		}
		return null;
	}

	public static String XML2HTML(final String xml, final String xslt)
			throws Exception {
		TransformerFactory tf = TransformerFactory.newInstance();

		InputStream stream = new FileInputStream(xslt);
		BufferedReader in = new BufferedReader(new InputStreamReader(stream));
		StringBuilder retval = new StringBuilder();
		String currLine = null;

		while ((currLine = in.readLine()) != null) {
			// a comment
			if (currLine.trim().length() > 0 && currLine.trim().startsWith("#")) {
				continue;
			}
			if (currLine.trim().length() == 0) { continue; }
			retval.append(currLine + System.getProperty("line.separator"));
		}
		in.close();


		StreamSource source = new StreamSource(new ByteArrayInputStream(retval.toString().getBytes()));
		Templates compiledXSLT = tf.newTemplates(source);
		Transformer t = compiledXSLT.newTransformer();
		t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "true");
		StringWriter w = new StringWriter();
		t.transform(new StreamSource(new StringReader(xml)), new StreamResult(w));
		return w.toString();
	}

}
