a little bit documentation
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / NameCatalogueController.java
index 82e91f0592db44167f12675cd5f97adc79516b67..15e993588afa53877c7e9d1acfca70e9eb78f21b 100755 (executable)
@@ -4,10 +4,12 @@ package eu.etaxonomy.cdm.remote.controller;
 import java.io.File;\r
 import java.io.IOException;\r
 import java.io.InputStream;\r
+import java.io.PrintWriter;\r
 import java.util.ArrayList;\r
 import java.util.Arrays;\r
 import java.util.HashMap;\r
 import java.util.HashSet;\r
+import java.util.Iterator;\r
 import java.util.LinkedHashMap;\r
 import java.util.List;\r
 import java.util.Map;\r
@@ -20,6 +22,10 @@ import javax.servlet.http.HttpServletResponse;
 \r
 import java.util.Hashtable;\r
 \r
+import org.apache.log4j.Level;\r
+import org.joda.time.DateTime;\r
+import org.joda.time.format.DateTimeFormat;\r
+import org.joda.time.format.DateTimeFormatter;\r
 import org.springframework.beans.factory.annotation.Autowired;\r
 import org.springframework.context.ResourceLoaderAware;\r
 import org.springframework.core.io.Resource;\r
@@ -31,8 +37,10 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.servlet.ModelAndView;\r
 \r
 import eu.etaxonomy.cdm.api.service.IClassificationService;\r
+import eu.etaxonomy.cdm.api.service.ICommonService;\r
 import eu.etaxonomy.cdm.api.service.INameService;\r
 import eu.etaxonomy.cdm.api.service.ITaxonService;\r
+import eu.etaxonomy.cdm.api.service.ITermService;\r
 import eu.etaxonomy.cdm.common.DocUtils;\r
 \r
 import eu.etaxonomy.cdm.remote.dto.common.ErrorResponse;\r
@@ -41,10 +49,16 @@ import eu.etaxonomy.cdm.remote.dto.namecatalogue.NameInformation;
 import eu.etaxonomy.cdm.remote.dto.namecatalogue.NameSearch;\r
 import eu.etaxonomy.cdm.remote.dto.namecatalogue.TaxonInformation;\r
 import eu.etaxonomy.cdm.remote.view.HtmlView;\r
+import eu.etaxonomy.cdm.model.common.CdmBase;\r
+import eu.etaxonomy.cdm.model.common.DefinedTermBase;\r
+import eu.etaxonomy.cdm.model.common.IdentifiableSource;\r
 import eu.etaxonomy.cdm.model.common.Language;\r
+import eu.etaxonomy.cdm.model.common.Representation;\r
+import eu.etaxonomy.cdm.model.description.Feature;\r
 import eu.etaxonomy.cdm.model.name.NonViralName;\r
 import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
 import eu.etaxonomy.cdm.model.reference.Reference;\r
+import eu.etaxonomy.cdm.model.taxon.Classification;\r
 import eu.etaxonomy.cdm.model.taxon.Synonym;\r
 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;\r
 import eu.etaxonomy.cdm.model.taxon.Taxon;\r
@@ -52,12 +66,14 @@ import eu.etaxonomy.cdm.model.taxon.TaxonBase;
 import eu.etaxonomy.cdm.model.taxon.TaxonNode;\r
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;\r
 import eu.etaxonomy.cdm.persistence.query.MatchMode;\r
+import eu.etaxonomy.cdm.persistence.query.OrderHint;\r
+import eu.etaxonomy.cdm.persistence.query.OrderHint.SortOrder;\r
 \r
 /**\r
- * The controller class for the namespace 'name_catalogue'. This web service namespace \r
- * is an add-on to the already existing CDM REST API and provides information relating \r
+ * The controller class for the namespace 'name_catalogue'. This web service namespace\r
+ * is an add-on to the already existing CDM REST API and provides information relating\r
  * to scientific names as well as taxa present in the underlying datasource.\r
- * \r
+ *\r
  * @author c.mathew\r
  * @version 1.1.0\r
  * @created 15-Apr-2012\r
@@ -71,7 +87,7 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
 \r
     /** Taxonomic status 'accepted' string */\r
     public static final String ACCEPTED_NAME_STATUS = "accepted";\r
-    \r
+\r
     /** Taxonpmic status 'synonym' string */\r
     public static final String SYNONYM_STATUS = "synonym";\r
 \r
@@ -80,71 +96,120 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
 \r
     /** Base scientific name search type */\r
     public static final String NAME_SEARCH = "name";\r
-    \r
+\r
     /** Complete scientific name search type */\r
     public static final String TITLE_SEARCH = "title";\r
-    \r
+\r
     /** Default name search type */\r
     public static final String DEFAULT_SEARCH_TYPE = NAME_SEARCH;\r
 \r
     /** Classifcation 'default' key */\r
     public static final String CLASSIFICATION_DEFAULT = "default";\r
-    \r
+\r
     /** Classifcation 'all' key */\r
     public static final String CLASSIFICATION_ALL = "all";\r
+    \r
+    private static final String DWC_DATASET_ID = "http://rs.tdwg.org/dwc/terms/datasetID";\r
 \r
+    private static final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd-MM-yyyy");\r
     @Autowired\r
     private ITaxonService taxonService;\r
-\r
+    \r
+    \r
     @Autowired\r
     private IClassificationService classificationService;\r
-\r
+    \r
+    @Autowired\r
+    private ICommonService commonService;\r
     /** Hibernate name search initialisation strategy */\r
     private static final List<String> NAME_SEARCH_INIT_STRATEGY = Arrays.asList(new String[] {\r
-            "combinationAuthorTeam.$", \r
-            "exCombinationAuthorTeam.$", \r
+            "combinationAuthorTeam.$",\r
+            "exCombinationAuthorTeam.$",\r
             "basionymAuthorTeam.$",\r
-            "exBasionymAuthorTeam.$", \r
-            "nameCache", \r
+            "exBasionymAuthorTeam.$",\r
+            "nameCache",\r
             "taxonBases",\r
-    "taxonBases.synonymRelations.type.$"});\r
+            "taxonBases.synonymRelations.type.$"});\r
 \r
     /** Hibernate name information initialisation strategy */\r
-    private static final List<String> NAME_INFORMATION_INIT_STRATEGY = Arrays.asList(new String[] { \r
+    private static final List<String> NAME_INFORMATION_INIT_STRATEGY = Arrays.asList(new String[] {\r
             "taxonBases",\r
-            "status", \r
-            "nomenclaturalReference.$", \r
-            "combinationAuthorTeam.$", \r
+            "status",\r
+            "nomenclaturalReference.$",\r
+            "combinationAuthorTeam.$",\r
             "exCombinationAuthorTeam.$",\r
-            "basionymAuthorTeam.$", \r
-            "exBasionymAuthorTeam.$", \r
-            "relationsToThisName.$",\r
-    "relationsFromThisName.$" });\r
+            "basionymAuthorTeam.$",\r
+            "exBasionymAuthorTeam.$",\r
+            "relationsToThisName.fromName.$",\r
+            "relationsToThisName.nomenclaturalReference.$",\r
+            "relationsToThisName.type.$",\r
+            "relationsFromThisName.toName.$",\r
+            "relationsFromThisName.nomenclaturalReference.$",\r
+            "relationsFromThisName.type.$"});\r
 \r
     /** Hibernate taxon information initialisation strategy */\r
     private static final List<String> TAXON_INFORMATION_INIT_STRATEGY = Arrays.asList(new String[] {\r
-            "synonymRelations.type.$", \r
-            "relationsFromThisTaxon.type.$", \r
+            "name.titleCache",\r
+            "name.rank.titleCache",    \r
+            \r
+            "sec.updated",\r
+            "sec.titleCache",\r
+            "sources.citation.sources.idNamespace",\r
+            "sources.citation.sources.idInSource",\r
+            \r
+            "synonymRelations.synonym.name.rank.titleCache",\r
+            "synonymRelations.synonym.sec.updated",\r
+            "synonymRelations.synonym.sec.titleCache",            \r
+            "synonymRelations.synonym.sources.citation.sources.idNamespace",\r
+            "synonymRelations.synonym.sources.citation.sources.idInSource",   \r
+            "synonymRelations.acceptedTaxon.name.rank.titleCache",\r
+            "synonymRelations.acceptedTaxon.sec.titleCache",\r
+            "synonymRelations.acceptedTaxon.sources.citation.sources.idNamespace",\r
+            "synonymRelations.acceptedTaxon.sources.citation.sources.idInSource",    \r
+            "synonymRelations.type.$",\r
+            \r
+            "relationsFromThisTaxon.type.$",\r
+            "relationsFromThisTaxon.toTaxon.name.rank.titleCache",\r
+            "relationsFromThisTaxon.toTaxon.sec.updated",\r
+            "relationsFromThisTaxon.toTaxon.sec.titleCache",\r
+            "relationsFromThisTaxon.toTaxon.sources.citation.sources.idNamespace",\r
+            "relationsFromThisTaxon.toTaxon.sources.citation.sources.idInSource",\r
+            \r
             "relationsToThisTaxon.type.$",\r
-            "taxonNodes", \r
-    "taxonNodes.classification" });\r
+            "relationsToThisTaxon.fromTaxon.name.rank.titleCache",\r
+            "relationsToThisTaxon.fromTaxon.sec.updated",\r
+            "relationsToThisTaxon.fromTaxon.sec.titleCache",\r
+            "relationsToThisTaxon.fromTaxon.sources.citation.sources.idNamespace",\r
+            "relationsToThisTaxon.fromTaxon.sources.citation.sources.idInSource",\r
+            \r
+            "taxonNodes",\r
+            "taxonNodes.classification" });\r
 \r
     /** Hibernate taxon node initialisation strategy */\r
-    private static final List<String> TAXON_NODE_INIT_STRATEGY = Arrays.asList(new String[] { \r
+    private static final List<String> TAXON_NODE_INIT_STRATEGY = Arrays.asList(new String[] {\r
             "taxon.sec",\r
-            "taxon.name", \r
-            "classification", \r
+            "taxon.name",\r
+            "classification",\r
             "classification.reference.$",\r
-    "classification.reference.authorTeam.$" });\r
+            "classification.reference.authorTeam.$" });\r
 \r
+    /** Hibernate classification vocabulary initialisation strategy */\r
+    private static final List<String> VOC_CLASSIFICATION_INIT_STRATEGY = Arrays.asList(new String[] {\r
+            "classification",\r
+            "classification.reference.$",\r
+            "classification.reference.authorTeam.$" });\r
+    \r
+    /** Hibernate classification vocabulary initialisation strategy */\r
+    private static final List<String> COMMON_INIT_STRATEGY = Arrays.asList(new String[] {});\r
+    \r
     public NameCatalogueController() {\r
         super();\r
-        setInitializationStrategy(Arrays.asList(new String[] { "$" })); \r
+        setInitializationStrategy(Arrays.asList(new String[] { "$" }));\r
     }\r
 \r
     /*\r
      * (non-Javadoc)\r
-     * \r
+     *\r
      * @see\r
      * eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu\r
      * .etaxonomy.cdm.api.service.IService)\r
@@ -159,7 +224,7 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
      * Returns a documentation page for the Name Search API.\r
      * <p>\r
      * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>\r
-     * \r
+     *\r
      * @param request\r
      * @param response\r
      * @return Html page describing the Name Search API\r
@@ -169,21 +234,20 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
     public ModelAndView doGetNameSearchDocumentation(\r
             HttpServletRequest request, HttpServletResponse response)\r
             throws IOException {\r
-        ModelAndView mv = new ModelAndView();               \r
+        ModelAndView mv = new ModelAndView();\r
         // Read apt documentation file.\r
-        Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-default.apt");        \r
+        Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-default.apt");\r
         // using input stream as this works for both files in the classes directory\r
         // as well as files inside jars\r
         InputStream aptInputStream = resource.getInputStream();\r
         // Build Html View\r
-        HtmlView hv = new HtmlView();\r
         Map<String, String> modelMap = new HashMap<String, String>();\r
-        modelMap.put("title", "Name Search API");\r
         // Convert Apt to Html\r
-        modelMap.put("body", DocUtils.convertAptToHtml(aptInputStream));\r
+        modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));\r
         mv.addAllObjects(modelMap);\r
-        mv.setView(hv);\r
 \r
+        HtmlView hv = new HtmlView();\r
+        mv.setView(hv);\r
         return mv;\r
     }\r
 \r
@@ -195,7 +259,7 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
      * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>\r
      * <p>\r
      * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>\r
-     * \r
+     *\r
      * @param queries\r
      *                The base scientific name pattern(s) to query for. The query can\r
      *                contain wildcard characters ('*'). The query can be\r
@@ -223,20 +287,20 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
      * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>\r
      * <p>\r
      * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>\r
-     * \r
+     *\r
      * @param query\r
      *                The scientific name pattern(s) to query for. The query can\r
      *                contain wildcard characters ('*'). The query can be\r
      *                performed with no wildcard or with the wildcard at the\r
      *                begin and / or end depending on the search pattern.\r
      * @param type\r
-     *                The type of name to query. This be either \r
+     *                The type of name to query. This be either\r
      *                "name" : scientific name corresponding to 'name cache' in CDM or\r
      *                "title" : complete name corresponding to 'title cache' in CDM\r
      * @param request Http servlet request.\r
      * @param response Http servlet response.\r
      * @return a List of {@link NameSearch} objects each corresponding to a\r
-     *         single query. These are built from {@link TaxonNameBase} entities \r
+     *         single query. These are built from {@link TaxonNameBase} entities\r
      *         which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}\r
      * @throws IOException\r
      */\r
@@ -264,7 +328,7 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
             logger.info("doGetNameSearch()" + request.getServletPath() + " for query \"" + query\r
                     + "\" without wild cards : " + queryWOWildcards + " and match mode : " + mm);\r
             List<NonViralName> nameList = new ArrayList<NonViralName>();\r
-            \r
+\r
             // if "name" search then find by name cache\r
             if (searchType.equals(NAME_SEARCH)) {\r
                 nameList = (List<NonViralName>) service.findNamesByNameCache(queryWOWildcards, mm,\r
@@ -276,16 +340,16 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
                 nameList = (List<NonViralName>) service.findNamesByTitleCache(queryWOWildcards, mm,\r
                         NAME_SEARCH_INIT_STRATEGY);\r
             }\r
-            \r
+\r
             // if search is successful then get related information , else return error\r
             if (nameList == null || !nameList.isEmpty()) {\r
                 NameSearch ns = new NameSearch();\r
                 ns.setRequest(query);\r
 \r
                 for (NonViralName nvn : nameList) {\r
-                    // we need to retrieve both taxon uuid of name queried and \r
+                    // we need to retrieve both taxon uuid of name queried and\r
                     // the corresponding accepted taxa.\r
-                    // reason to return accepted taxa also, is to be able to get from \r
+                    // reason to return accepted taxa also, is to be able to get from\r
                     // scientific name to taxon concept in two web service calls.\r
                     Set<TaxonBase> tbSet = nvn.getTaxonBases();\r
                     Set<TaxonBase> accTbSet = new HashSet<TaxonBase>();\r
@@ -295,7 +359,7 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
                             Synonym synonym = (Synonym) tb;\r
                             Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();\r
                             for (SynonymRelationship sr : synRelationships) {\r
-                                Taxon accTaxon = sr.getAcceptedTaxon();                                                                \r
+                                Taxon accTaxon = sr.getAcceptedTaxon();\r
                                 accTbSet.add(accTaxon);\r
                             }\r
                         } else {\r
@@ -313,17 +377,18 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
                 er.setErrorMessage("No Taxon Name for given query : " + query);\r
                 nsList.add(er);\r
             }\r
-        }\r
+        }        \r
+\r
         mv.addObject(nsList);\r
         return mv;\r
     }\r
 \r
-    \r
+\r
     /**\r
      * Returns a documentation page for the Name Information API.\r
      * <p>\r
      * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/name</b>\r
-     * \r
+     *\r
      * @param request Http servlet request.\r
      * @param response Http servlet response.\r
      * @return Html page describing the Name Information API\r
@@ -340,17 +405,16 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
         // as well as files inside jars\r
         InputStream aptInputStream = resource.getInputStream();\r
         // Build Html View\r
-        HtmlView hv = new HtmlView();\r
         Map<String, String> modelMap = new HashMap<String, String>();\r
-        modelMap.put("title", "Name Information API");\r
         // Convert Apt to Html\r
-        modelMap.put("body", DocUtils.convertAptToHtml(aptInputStream));\r
+        modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));\r
         mv.addAllObjects(modelMap);\r
-        mv.setView(hv);\r
 \r
+        HtmlView hv = new HtmlView();\r
+        mv.setView(hv);\r
         return mv;\r
     }\r
-    \r
+\r
     /**\r
      * Returns information related to the scientific name matching the given\r
      * <code>{nameUuid}</code>. The information includes the name string,\r
@@ -359,12 +423,12 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
      * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-name-info.html">here</a>\r
      * <p>\r
      * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/name</b>\r
-     * \r
+     *\r
      * @param nameUuids uuid(s) of the scientific name to search for.\r
      * @param request Http servlet request.\r
      * @param response Http servlet response.\r
      * @return a List of {@link NameInformation} objects each corresponding to a\r
-     *         single name uuid. These are built from {@link TaxonNameBase} entities \r
+     *         single name uuid. These are built from {@link TaxonNameBase} entities\r
      *         which are in turn initialized using the {@link #NAME_INFORMATION_INIT_STRATEGY}\r
      * @throws IOException\r
      */\r
@@ -379,8 +443,8 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
                     + nameUuid + "\"");\r
             // find name by uuid\r
             NonViralName nvn = (NonViralName) service.findNameByUuid(UUID.fromString(nameUuid),\r
-                    NAME_INFORMATION_INIT_STRATEGY);\r
-            \r
+                        NAME_INFORMATION_INIT_STRATEGY);\r
+\r
             // if search is successful then get related information, else return error\r
             if (nvn != null) {\r
                 NameInformation ni = new NameInformation();\r
@@ -398,7 +462,12 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
                 niList.add(ni);\r
             } else {\r
                 ErrorResponse re = new ErrorResponse();\r
-                re.setErrorMessage("No Taxon Name for given UUID : " + nameUuid);\r
+\r
+                if(isValid(nameUuid)) {\r
+                    re.setErrorMessage("No Name for given UUID : " + nameUuid);\r
+                } else {\r
+                    re.setErrorMessage(nameUuid + " not a valid UUID");\r
+                }\r
                 niList.add(re);\r
             }\r
         }\r
@@ -410,7 +479,7 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
      * Returns a documentation page for the Taxon Information API.\r
      * <p>\r
      * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/taxon</b>\r
-     * \r
+     *\r
      * @param request Http servlet request.\r
      * @param response Http servlet response.\r
      * @return Html page describing the Taxon Information API\r
@@ -427,35 +496,34 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
         // as well as files inside jars\r
         InputStream aptInputStream = resource.getInputStream();\r
         // Build Html View\r
-        HtmlView hv = new HtmlView();\r
         Map<String, String> modelMap = new HashMap<String, String>();\r
-        modelMap.put("title", "Taxon Information API");\r
         // Convert Apt to Html\r
-        modelMap.put("body", DocUtils.convertAptToHtml(aptInputStream));\r
+        modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));\r
         mv.addAllObjects(modelMap);\r
-        mv.setView(hv);\r
 \r
+        HtmlView hv = new HtmlView();\r
+        mv.setView(hv);\r
         return mv;\r
     }\r
-    \r
+\r
     /**\r
      * Returns information related to the taxon matching the given\r
-     * <code>{taxonUuid}</code>. The information includes the name cache, title cache \r
+     * <code>{taxonUuid}</code>. The information includes the name cache, title cache\r
      * relationship type, taxonomic status, information , etc.\r
      * <p>\r
      * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>\r
      * <p>\r
      * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>\r
-     * \r
+     *\r
      * @param taxonUuid\r
-     *                 The taxon uuid to query for. The classification returned corresponds \r
-     *                 to the first in the alphabetically sorted list of classifications \r
-     *                 currently available in the database.  \r
-     *                \r
+     *                 The taxon uuid to query for. The classification returned corresponds\r
+     *                 to the first in the alphabetically sorted list of classifications\r
+     *                 currently available in the database.\r
+     *\r
      * @param request Http servlet request.\r
      * @param response Http servlet response.\r
      * @return a List of {@link TaxonInformation} objects each corresponding to a\r
-     *         single query. These are built from {@TaxonBase} entities which are \r
+     *         single query. These are built from {@TaxonBase} entities which are\r
      *         in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}\r
      * @throws IOException\r
      */\r
@@ -463,29 +531,29 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
     public ModelAndView doGetTaxonInformation(\r
             @RequestParam(value = "taxonUuid", required = true) String[] taxonUuids,\r
             HttpServletRequest request, HttpServletResponse response) throws IOException {\r
-        return doGetTaxonInformation(taxonUuids,CLASSIFICATION_DEFAULT, request, response);    \r
+        return doGetTaxonInformation(taxonUuids,CLASSIFICATION_DEFAULT, request, response);\r
     }\r
-    \r
+\r
     /**\r
      * Returns information related to the taxon matching the given\r
-     * <code>{taxonUuid}</code>. The information includes the name cache, title cache \r
+     * <code>{taxonUuid}</code>. The information includes the name cache, title cache\r
      * relationship type, taxonomic status, information , etc.\r
      * <p>\r
      * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>\r
      * <p>\r
      * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/taxon</b>\r
-     * \r
+     *\r
      * @param taxonUuid\r
      *                 The taxon uuid to query for.\r
-     * @param classification \r
-     *                 [Optional] String representing the taxonomic classification to use for \r
-     *                 building the classification tree. Defaults to the first in the alphabetically \r
-     *                 sorted list of classifications currently available in the database.  \r
-     *                \r
+     * @param classification\r
+     *                 [Optional] String representing the taxonomic classification to use for\r
+     *                 building the classification tree. Defaults to the first in the alphabetically\r
+     *                 sorted list of classifications currently available in the database.\r
+     *\r
      * @param request Http servlet request.\r
      * @param response Http servlet response.\r
      * @return a List of {@link TaxonInformation} objects each corresponding to a\r
-     *         single query. These are built from {@TaxonBase} entities which are \r
+     *         single query. These are built from {@TaxonBase} entities which are\r
      *         in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}\r
      * @throws IOException\r
      */\r
@@ -503,6 +571,7 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
             // find name by uuid\r
             TaxonBase tb = taxonService.findTaxonByUuid(UUID.fromString(taxonUuid),\r
                     TAXON_INFORMATION_INIT_STRATEGY);\r
+            \r
             // if search is successful then get related information, else return error\r
             if (tb != null) {\r
                 TaxonInformation ti = new TaxonInformation();\r
@@ -511,80 +580,215 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
                 if (tb.isInstanceOf(Taxon.class)) {\r
                     Taxon taxon = (Taxon) tb;\r
                     // build classification map\r
-                    Map classificationMap = buildClassificationMap(taxon, classificationType);\r
-                    if (classificationMap == null) {\r
-                        ErrorResponse er = new ErrorResponse();\r
-                        er.setErrorMessage("Invalid classification type");\r
-                        mv.addObject(er);\r
-                        return mv;\r
-                    }\r
+                    Map classificationMap = getClassification(taxon, classificationType);\r
 \r
-                    if (classificationMap.isEmpty()) {\r
-                        ErrorResponse er = new ErrorResponse();\r
-                        er.setErrorMessage("No classification available for requested type");\r
-                        mv.addObject(er);\r
-                        return mv;\r
-                    }\r
+                    logger.info("taxon uuid " + taxon.getUuid().toString() + " original hash code : " + System.identityHashCode(taxon) + ", name class " + taxon.getName().getClass().getName());
                     // update taxon information object with taxon related data\r
-                    ti.setResponseTaxon(tb.getTitleCache(), ACCEPTED_NAME_STATUS, buildFlagMap(tb),\r
-                            classificationMap);\r
+                    NonViralName nvn = (NonViralName) taxon.getName();\r
+\r
+                    String secTitle = "" ;\r
+                    String modified = "";\r
+                    if(taxon.getSec() != null) {\r
+                       secTitle = taxon.getSec().getTitleCache();\r
+                       DateTime dt = taxon.getUpdated();                               \r
+                        modified = fmt.print(dt);\r
+                    }\r
+                    \r
+                    Set<IdentifiableSource> sources = taxon.getSources();\r
+                    String[] didname = getDatasetIdName(sources);                    \r
+\r
+                    ti.setResponseTaxon(tb.getTitleCache(),\r
+                            nvn.getTitleCache(),\r
+                            nvn.getRank().getTitleCache(),\r
+                            ACCEPTED_NAME_STATUS,\r
+                            buildFlagMap(tb),\r
+                            classificationMap,\r
+                            "",\r
+                            didname[0],\r
+                            didname[1],\r
+                            secTitle,\r
+                            modified);\r
+                    \r
+                    \r
                     Set<SynonymRelationship> synRelationships = taxon.getSynonymRelations();\r
                     // add synonyms (if exists) to taxon information object\r
                     for (SynonymRelationship sr : synRelationships) {\r
                         Synonym syn = sr.getSynonym();\r
                         String uuid = syn.getUuid().toString();\r
                         String title = syn.getTitleCache();\r
+                        TaxonNameBase synnvn = (TaxonNameBase) syn.getName();\r
+                        String name = synnvn.getTitleCache();\r
+                        String rank = synnvn.getRank().getTitleCache();\r
                         String status = SYNONYM_STATUS;\r
                         String relLabel = sr.getType()\r
                                 .getInverseRepresentation(Language.DEFAULT())\r
                                 .getLabel();\r
-                        ti.addToResponseRelatedTaxa(uuid, title, status, "", relLabel);\r
+                        \r
+                        secTitle = "" ;\r
+                        modified = "";\r
+                        if(syn.getSec() != null) {\r
+                               secTitle = syn.getSec().getTitleCache();\r
+                               DateTime dt = syn.getUpdated();                               \r
+                            modified = fmt.print(dt);\r
+                        }\r
+                        \r
+                        sources = syn.getSources();\r
+                        didname = getDatasetIdName(sources);\r
+                                                \r
+                        ti.addToResponseRelatedTaxa(uuid, \r
+                                       title, \r
+                                       name, \r
+                                       rank, \r
+                                       status, \r
+                                       relLabel,\r
+                                       "",\r
+                                didname[0],\r
+                                didname[1],\r
+                                       secTitle,\r
+                                modified);\r
                     }\r
 \r
                     // build relationship information as,\r
                     // - relationships from the requested taxon\r
-                    Set<TaxonRelationship> trFromSet = taxon.getRelationsFromThisTaxon();                   \r
-                    for (TaxonRelationship tr : trFromSet) {\r
-                        String titleTo = tr.getRelatedTo().getTitleCache();\r
-                        String uuid = tr.getRelatedTo().getUuid().toString();\r
+                    Set<TaxonRelationship> trFromSet = taxon.getRelationsFromThisTaxon();\r
+                    for (TaxonRelationship tr : trFromSet) {                        \r
+                        String titleTo = tr.getToTaxon().getTitleCache();\r
+                        TaxonNameBase tonvn = (TaxonNameBase) tr.getToTaxon().getName();\r
+                        String name = tonvn.getTitleCache();\r
+                        String rank = tonvn.getRank().getTitleCache();\r
+                        String uuid = tr.getToTaxon().getUuid().toString();\r
                         String status = ACCEPTED_NAME_STATUS;\r
                         String relLabel = tr.getType().getRepresentation(Language.DEFAULT())\r
                                 .getLabel();\r
-                        ti.addToResponseRelatedTaxa(uuid, titleTo, status, "", relLabel);\r
+\r
+                        secTitle = "" ;\r
+                        modified = "";\r
+                        if(tr.getToTaxon().getSec() != null) {\r
+                               secTitle = tr.getToTaxon().getSec().getTitleCache();\r
+                               DateTime dt = tr.getToTaxon().getUpdated();                               \r
+                            modified = fmt.print(dt);\r
+                        }\r
+                        \r
+                        sources = tr.getToTaxon().getSources();\r
+                        didname = getDatasetIdName(sources);\r
+                                                \r
+                        ti.addToResponseRelatedTaxa(uuid, \r
+                                       titleTo, \r
+                                       name, \r
+                                       rank, \r
+                                       status, \r
+                                       relLabel,\r
+                                       "",\r
+                                didname[0],\r
+                                didname[1],\r
+                                       secTitle,\r
+                                modified);\r
+                        //logger.info("titleTo : " + titleTo + " , name : " + name);\r
                     }\r
 \r
-                    // - relationships from the requested taxon\r
+                    // - relationships to the requested taxon\r
                     Set<TaxonRelationship> trToSet = taxon.getRelationsToThisTaxon();\r
                     for (TaxonRelationship tr : trToSet) {\r
-                        String titleFrom = tr.getRelatedFrom().getTitleCache();\r
-                        String uuid = tr.getRelatedFrom().getUuid().toString();\r
+                        String titleFrom = tr.getFromTaxon().getTitleCache();\r
+                        TaxonNameBase fromnvn = (TaxonNameBase) tr.getFromTaxon().getName();\r
+                        String name = fromnvn.getTitleCache();\r
+                        String rank = fromnvn.getRank().getTitleCache();\r
+                        String uuid = tr.getFromTaxon().getUuid().toString();\r
                         String status = ACCEPTED_NAME_STATUS;\r
                         String relLabel = tr.getType()\r
                                 .getInverseRepresentation(Language.DEFAULT())\r
                                 .getLabel();\r
-                        ti.addToResponseRelatedTaxa(uuid, titleFrom, status, "", relLabel);\r
+\r
+                        if(tr.getFromTaxon().getSec() != null) {\r
+                               secTitle = tr.getFromTaxon().getSec().getTitleCache();\r
+                               DateTime dt = tr.getFromTaxon().getSec().getUpdated();                               \r
+                            modified = fmt.print(dt);\r
+                        }\r
+                        \r
+                        sources = tr.getFromTaxon().getSources();\r
+                        didname = getDatasetIdName(sources);\r
+                        \r
+                        secTitle = (tr.getFromTaxon().getSec() == null) ? "" : tr.getFromTaxon().getSec().getTitleCache();\r
+                        ti.addToResponseRelatedTaxa(uuid, \r
+                                       titleFrom, \r
+                                       name, \r
+                                       rank, \r
+                                       status,\r
+                                       relLabel,\r
+                                       "",\r
+                                didname[0],\r
+                                didname[1],\r
+                                       secTitle,\r
+                                modified);\r
+                        //logger.info("titleFrom : " + titleFrom + " , name : " + name);\r
                     }\r
                 } else if (tb instanceof Synonym) {\r
                     Synonym synonym = (Synonym) tb;\r
+                    TaxonNameBase nvn = (TaxonNameBase) synonym.getName();\r
                  // update taxon information object with synonym related data\r
-                    ti.setResponseTaxon(synonym.getTitleCache(), SYNONYM_STATUS,\r
-                            buildFlagMap(synonym), null);\r
+                    DateTime dt = synonym.getUpdated();                    \r
+                    String modified = fmt.print(dt);\r
+                    \r
+                    Set<IdentifiableSource> sources = synonym.getSources();\r
+                    String[] didname = getDatasetIdName(sources);\r
+                    \r
+                    String secTitle = (synonym.getSec() == null) ? "" : synonym.getSec().getTitleCache();\r
+                    ti.setResponseTaxon(synonym.getTitleCache(),\r
+                            nvn.getTitleCache(),\r
+                            nvn.getRank().getTitleCache(),\r
+                            SYNONYM_STATUS,\r
+                            buildFlagMap(synonym),\r
+                            new TreeMap<String,Map>(),\r
+                            "",\r
+                            didname[0],\r
+                            didname[1],\r
+                               secTitle,\r
+                            modified);\r
                     // add accepted taxa (if exists) to taxon information object\r
+                    \r
                     Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();\r
                     for (SynonymRelationship sr : synRelationships) {\r
-                        Taxon accTaxon = sr.getAcceptedTaxon();\r
+                        Taxon accTaxon = sr.getAcceptedTaxon();                        \r
                         String uuid = accTaxon.getUuid().toString();\r
+                        logger.info("acc taxon uuid " + accTaxon.getUuid().toString() + " original hash code : " + System.identityHashCode(accTaxon) + ", name class " + accTaxon.getName().getClass().getName());\r
                         String title = accTaxon.getTitleCache();\r
+                        logger.info("taxon title cache : " + accTaxon.getTitleCache());\r
+                                           \r
+                        TaxonNameBase accnvn = (TaxonNameBase)accTaxon.getName();\r
+                        String name = accnvn.getTitleCache();\r
+                        String rank = accnvn.getRank().getTitleCache();\r
                         String status = ACCEPTED_NAME_STATUS;\r
                         String relLabel = sr.getType().getRepresentation(Language.DEFAULT())\r
                                 .getLabel();\r
-                        ti.addToResponseRelatedTaxa(uuid, title, status, "", relLabel);\r
+                        dt = accTaxon.getUpdated();                    \r
+                        modified = fmt.print(dt);\r
+                        \r
+                        sources = accTaxon.getSources();\r
+                        didname = getDatasetIdName(sources);\r
+                        \r
+                        secTitle = (accTaxon.getSec() == null) ? "" : accTaxon.getSec().getTitleCache();\r
+                        ti.addToResponseRelatedTaxa(uuid, \r
+                                       title, \r
+                                       name, \r
+                                       rank, \r
+                                       status, \r
+                                       relLabel,\r
+                                       "",\r
+                                didname[0],\r
+                                didname[1],\r
+                                       secTitle,\r
+                                modified);\r
                     }\r
+\r
                 }\r
                 tiList.add(ti);\r
             } else {\r
                 ErrorResponse re = new ErrorResponse();\r
-                re.setErrorMessage("No Taxon for given UUID : " + taxonUuid);\r
+                if(isValid(taxonUuid)) {\r
+                    re.setErrorMessage("No Taxon for given UUID : " + taxonUuid);\r
+                } else {\r
+                    re.setErrorMessage(taxonUuid + " not a valid UUID");\r
+                }\r
                 tiList.add(re);\r
             }\r
         }\r
@@ -593,11 +797,92 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
     }\r
 \r
     /**\r
-     * Returns the match mode by parsing the input string of wildcards.\r
+     * Returns a list of all available classifications (with associated referenc information) and the default classification.\r
+     * <p>\r
+     * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-classification-info.html">here</a>\r
+     * <p>\r
+     * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/voc/classification</b>\r
+     *\r
+     * @param request Http servlet request.\r
+     * @param response Http servlet response.\r
+     * @return a List of {@link Classification} objects represebted as strings.\r
+     *         These are initialized using the {@link #VOC_CLASSIFICATION_INIT_STRATEGY}\r
+     * @throws IOException\r
+     */\r
+    @RequestMapping(value = { "voc/classification" }, method = RequestMethod.GET, params = {})\r
+    public ModelAndView doGetClassificationMap(HttpServletRequest request, HttpServletResponse response) throws IOException {\r
+        List<Map> cmapList = new ArrayList<Map>();\r
+        Map<String, String> classifications = new HashMap<String, String>();\r
+        ModelAndView mv = new ModelAndView();\r
+        List<Classification> clist = getClassificationList(100);\r
+        boolean isFirst = true;\r
+        Iterator itr = clist.iterator();\r
+        // loop through all classifications and populate map with\r
+        // (classificationKey, reference) elements\r
+        while(itr.hasNext()) {\r
+            Classification c = (Classification) itr.next();\r
+            String refTitleCache = "";\r
+            String classificationKey = removeInternalWhitespace(c.getTitleCache());\r
+            if(c.getReference() != null) {\r
+                refTitleCache = c.getReference().getTitleCache();\r
+            }\r
+            // default is the first element of the list\r
+            // always created with the same sorting (DESCENDING)\r
+            if(isFirst) {\r
+                Map<String, String> defaultMap = new HashMap<String, String>();\r
+                defaultMap.put("default", classificationKey);\r
+                cmapList.add(defaultMap);\r
+                isFirst = false;\r
+            }\r
+            classifications.put(classificationKey, refTitleCache);\r
+\r
+        }\r
+        Map<String, Map> cmap = new HashMap<String, Map>();\r
+        cmap.put("classification",classifications);\r
+        cmapList.add(cmap);\r
+        mv.addObject(cmapList);\r
+        return mv;\r
+    }\r
+\r
+    /**\r
+     * Returns the Dataset ID / Name of the given original source.\r
+     * FIXME: Very hacky and needs to be revisited. Mainly for deciding on which objects to use during import.\r
+     * FIXME: dataset id is mapped to a DWC term - is that right?\r
      * \r
+     * @param sources Set of sources attached to a taxa / synonym\r
+     *             \r
+     *\r
+     * @return String array where [0] is the datsetID and [1] is the datsetName\r
+     */\r
+    private String[] getDatasetIdName(Set<IdentifiableSource> sources) {\r
+       String didname[] = {"",""};\r
+        Iterator<IdentifiableSource> itr = sources.iterator();\r
+        while(itr.hasNext()) {\r
+               IdentifiableSource source = itr.next();         \r
+               Reference ref = source.getCitation();              \r
+            Set<IdentifiableSource> ref_sources = ref.getSources();\r
+            Iterator<IdentifiableSource> ref_itr = ref_sources.iterator();\r
+            while(ref_itr.hasNext()) {\r
+               IdentifiableSource ref_source = ref_itr.next();                 \r
+               if(ref_source.getIdNamespace().equals(DWC_DATASET_ID)) {\r
+                       didname[0] = ref_source.getIdInSource();\r
+                       break;\r
+               }\r
+            }\r
+            if(!didname[0].isEmpty()) {\r
+               didname[1] = ref.getTitleCache();\r
+               break;\r
+            }\r
+        }\r
+       return didname;\r
+    }\r
+    \r
+    /**\r
+     * Returns the match mode by parsing the input string of wildcards.\r
+     *\r
      * @param query\r
      *             String to parse.\r
-     *                \r
+     *\r
      * @return {@link MatchMode} depending on the the position of the wildcard (*)\r
      */\r
     private MatchMode getMatchModeFromQuery(String query) {\r
@@ -614,10 +899,10 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
 \r
     /**\r
      * Removes wildcards from the input string.\r
-     * \r
+     *\r
      * @param query\r
      *             String to parse.\r
-     *                \r
+     *\r
      * @return input string with wildcards removed\r
      */\r
     private String getQueryWithoutWildCards(String query) {\r
@@ -636,7 +921,7 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
     }\r
 \r
     /**\r
-     * Build map with taxon flag key-value pairs.    \r
+     * Build map with taxon flag key-value pairs.\r
      */\r
     private Map<String, String> buildFlagMap(TaxonBase tb) {\r
         Map<String, String> flags = new Hashtable<String, String>();\r
@@ -644,11 +929,35 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
         return flags;\r
     }\r
 \r
+    /**\r
+     * Build classification map.\r
+     */
+    private Map<String, Map> getClassification(Taxon taxon, String classificationType) {\r
+        // Using TreeMap is important, because we need the sorting of the classification keys\r
+        // in the map to be stable.\r
+        TreeMap<String, Map> sourceClassificationMap = buildClassificationMap(taxon, classificationType);\r
+\r
+        // if classification key is 'default' then return the default element of the map\r
+        if(classificationType.equals(CLASSIFICATION_DEFAULT) && !sourceClassificationMap.isEmpty()) {\r
+            List<Classification> clist = getClassificationList(1);\r
+            String defaultKey = removeInternalWhitespace(clist.get(0).getTitleCache());\r
+            return sourceClassificationMap.get(defaultKey);\r
+            // if classification key is provided then return the classification corresponding to the key\r
+        } else if(sourceClassificationMap.containsKey(classificationType)) {\r
+            return sourceClassificationMap.get(classificationType);\r
+            // if classification key is 'all' then return the entire map\r
+        } else if(classificationType.equals(CLASSIFICATION_ALL)) {\r
+            return sourceClassificationMap;\r
+        } else {\r
+            return new TreeMap<String,Map>();\r
+        }\r
+    }\r
+\r
     /**\r
      * Build classification map.\r
      */\r
-    private Map<String, Map> buildClassificationMap(Taxon taxon, String classificationType) {\r
-        // Using TreeMap is important, because we need the sorting of the classification keys \r
+    private TreeMap<String, Map> buildClassificationMap(Taxon taxon, String classificationType) {\r
+        // Using TreeMap is important, because we need the sorting of the classification keys
         // in the map to be stable.\r
         TreeMap<String, Map> sourceClassificationMap = new TreeMap<String, Map>();\r
         Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();\r
@@ -658,37 +967,49 @@ public class NameCatalogueController extends BaseController<TaxonNameBase, IName
             List<TaxonNode> tnList = classificationService.loadTreeBranchToTaxon(taxon,\r
                     tn.getClassification(), null, TAXON_NODE_INIT_STRATEGY);\r
             for (TaxonNode classificationtn : tnList) {\r
-\r
                 classificationMap.put(classificationtn.getTaxon().getName().getRank().getTitleCache(),\r
                         classificationtn.getTaxon().getName().getTitleCache());\r
             }\r
-\r
-            String cname = tn.getClassification().getTitleCache();\r
-            String[] words = cname.split("\\s+");\r
-            // "\\s+" in regular expression language meaning one or\r
-            // more spaces\r
-            StringBuilder builder = new StringBuilder();\r
-            for (String word : words) {\r
-                builder.append(word);\r
-            }\r
-            cname = builder.toString();\r
+            String cname = removeInternalWhitespace(tn.getClassification().getTitleCache());\r
+            logger.info("Building classification map " + cname);\r
             sourceClassificationMap.put(cname, classificationMap);\r
         }\r
+        return sourceClassificationMap;\r
+    }\r
 \r
-        // if classification key is 'default' then return the first element of the map\r
-        if(classificationType.equals(CLASSIFICATION_DEFAULT) && !sourceClassificationMap.isEmpty()) {\r
-            return sourceClassificationMap.firstEntry().getValue();\r
-            // if classification key is provided then return the classification corresponding to the key\r
-        } else if(sourceClassificationMap.containsKey(classificationType)) {\r
-            return sourceClassificationMap.get(classificationType);\r
-            // if classification key is 'all' then return the entire map\r
-        } else if(classificationType.equals(CLASSIFICATION_ALL)) {\r
-            return sourceClassificationMap;\r
-        } else {\r
-            return null;\r
+    private String removeInternalWhitespace(String withWSpace) {\r
+        String[] words = withWSpace.split("\\s+");\r
+        // "\\s+" in regular expression language meaning one or\r
+        // more spaces\r
+        StringBuilder builder = new StringBuilder();\r
+        for (String word : words) {\r
+            builder.append(word);\r
         }\r
+        return builder.toString();\r
+    }\r
+\r
+    private List<Classification> getClassificationList(int limit) {\r
+        List<OrderHint> orderHints = new ArrayList<OrderHint>();\r
+        orderHints.add(new OrderHint("titleCache", SortOrder.DESCENDING));\r
+        List<Classification> clist = classificationService.listClassifications(limit, 0, orderHints, VOC_CLASSIFICATION_INIT_STRATEGY);\r
+        return clist;\r
     }\r
 \r
+    private boolean isValid(String uuid){\r
+        if( uuid == null) return false;\r
+        try {\r
+            // we have to convert to object and back to string because the built in fromString does not have\r
+            // good validation logic.\r
+\r
+            UUID fromStringUUID = UUID.fromString(uuid);\r
+            String toStringUUID = fromStringUUID.toString();\r
+\r
+            System.out.println("input uuid : " + uuid + " , parsed uuid : " + toStringUUID);\r
+            return toStringUUID.equals(uuid);\r
+        } catch(IllegalArgumentException e) {\r
+            return false;\r
+        }\r
+    }\r
     @Override\r
     public void setResourceLoader(ResourceLoader resourceLoader) {\r
         this.resourceLoader = resourceLoader;\r