2 package eu
.etaxonomy
.cdm
.remote
.controller
;
5 import java
.io
.IOException
;
6 import java
.io
.InputStream
;
7 import java
.io
.PrintWriter
;
8 import java
.util
.ArrayList
;
9 import java
.util
.Arrays
;
10 import java
.util
.HashMap
;
11 import java
.util
.HashSet
;
12 import java
.util
.Iterator
;
13 import java
.util
.LinkedHashMap
;
14 import java
.util
.List
;
17 import java
.util
.TreeMap
;
18 import java
.util
.UUID
;
20 import javax
.servlet
.http
.HttpServletRequest
;
21 import javax
.servlet
.http
.HttpServletResponse
;
23 import java
.util
.Hashtable
;
25 import org
.apache
.log4j
.Level
;
26 import org
.joda
.time
.DateTime
;
27 import org
.joda
.time
.format
.DateTimeFormat
;
28 import org
.joda
.time
.format
.DateTimeFormatter
;
29 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
30 import org
.springframework
.context
.ResourceLoaderAware
;
31 import org
.springframework
.core
.io
.Resource
;
32 import org
.springframework
.core
.io
.ResourceLoader
;
33 import org
.springframework
.stereotype
.Controller
;
34 import org
.springframework
.web
.bind
.annotation
.RequestMapping
;
35 import org
.springframework
.web
.bind
.annotation
.RequestMethod
;
36 import org
.springframework
.web
.bind
.annotation
.RequestParam
;
37 import org
.springframework
.web
.servlet
.ModelAndView
;
39 import eu
.etaxonomy
.cdm
.api
.service
.IClassificationService
;
40 import eu
.etaxonomy
.cdm
.api
.service
.ICommonService
;
41 import eu
.etaxonomy
.cdm
.api
.service
.INameService
;
42 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonService
;
43 import eu
.etaxonomy
.cdm
.api
.service
.ITermService
;
44 import eu
.etaxonomy
.cdm
.common
.DocUtils
;
46 import eu
.etaxonomy
.cdm
.remote
.dto
.common
.ErrorResponse
;
47 import eu
.etaxonomy
.cdm
.remote
.dto
.common
.RemoteResponse
;
48 import eu
.etaxonomy
.cdm
.remote
.dto
.namecatalogue
.NameInformation
;
49 import eu
.etaxonomy
.cdm
.remote
.dto
.namecatalogue
.NameSearch
;
50 import eu
.etaxonomy
.cdm
.remote
.dto
.namecatalogue
.TaxonInformation
;
51 import eu
.etaxonomy
.cdm
.remote
.view
.HtmlView
;
52 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
53 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTermBase
;
54 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
55 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
56 import eu
.etaxonomy
.cdm
.model
.common
.Representation
;
57 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
58 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
59 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
61 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
62 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
63 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
64 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
65 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
66 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
67 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
68 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
69 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
70 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
73 * The controller class for the namespace 'name_catalogue'. This web service namespace
74 * is an add-on to the already existing CDM REST API and provides information relating
75 * to scientific names as well as taxa present in the underlying datasource.
79 * @created 15-Apr-2012
83 @RequestMapping(value
= { "/name_catalogue" })
84 public class NameCatalogueController
extends BaseController
<TaxonNameBase
, INameService
> implements ResourceLoaderAware
{
86 private ResourceLoader resourceLoader
;
88 /** Taxonomic status 'accepted' string */
89 public static final String ACCEPTED_NAME_STATUS
= "accepted";
91 /** Taxonpmic status 'synonym' string */
92 public static final String SYNONYM_STATUS
= "synonym";
94 /** Flag 'doubtful' strings */
95 public static final String DOUBTFUL_FLAG
= "doubtful";
97 /** Base scientific name search type */
98 public static final String NAME_SEARCH
= "name";
100 /** Complete scientific name search type */
101 public static final String TITLE_SEARCH
= "title";
103 /** Default name search type */
104 public static final String DEFAULT_SEARCH_TYPE
= NAME_SEARCH
;
106 /** Classifcation 'default' key */
107 public static final String CLASSIFICATION_DEFAULT
= "default";
109 /** Classifcation 'all' key */
110 public static final String CLASSIFICATION_ALL
= "all";
112 private static final String DWC_DATASET_ID
= "http://rs.tdwg.org/dwc/terms/datasetID";
114 private static final DateTimeFormatter fmt
= DateTimeFormat
.forPattern("dd-MM-yyyy");
116 private ITaxonService taxonService
;
120 private IClassificationService classificationService
;
123 private ICommonService commonService
;
124 /** Hibernate name search initialisation strategy */
125 private static final List
<String
> NAME_SEARCH_INIT_STRATEGY
= Arrays
.asList(new String
[] {
126 "combinationAuthorTeam.$",
127 "exCombinationAuthorTeam.$",
128 "basionymAuthorTeam.$",
129 "exBasionymAuthorTeam.$",
132 "taxonBases.synonymRelations.type.$"});
134 /** Hibernate name information initialisation strategy */
135 private static final List
<String
> NAME_INFORMATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
138 "nomenclaturalReference.$",
139 "combinationAuthorTeam.$",
140 "exCombinationAuthorTeam.$",
141 "basionymAuthorTeam.$",
142 "exBasionymAuthorTeam.$",
143 "relationsToThisName.fromName.$",
144 "relationsToThisName.nomenclaturalReference.$",
145 "relationsToThisName.type.$",
146 "relationsFromThisName.toName.$",
147 "relationsFromThisName.nomenclaturalReference.$",
148 "relationsFromThisName.type.$"});
150 /** Hibernate taxon information initialisation strategy */
151 private static final List
<String
> TAXON_INFORMATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
153 "name.rank.titleCache",
157 "sources.citation.sources.idNamespace",
158 "sources.citation.sources.idInSource",
160 "synonymRelations.synonym.name.rank.titleCache",
161 "synonymRelations.synonym.sec.updated",
162 "synonymRelations.synonym.sec.titleCache",
163 "synonymRelations.synonym.sources.citation.sources.idNamespace",
164 "synonymRelations.synonym.sources.citation.sources.idInSource",
165 "synonymRelations.acceptedTaxon.name.rank.titleCache",
166 "synonymRelations.acceptedTaxon.sec.titleCache",
167 "synonymRelations.acceptedTaxon.sources.citation.sources.idNamespace",
168 "synonymRelations.acceptedTaxon.sources.citation.sources.idInSource",
169 "synonymRelations.type.$",
171 "relationsFromThisTaxon.type.$",
172 "relationsFromThisTaxon.toTaxon.name.rank.titleCache",
173 "relationsFromThisTaxon.toTaxon.sec.updated",
174 "relationsFromThisTaxon.toTaxon.sec.titleCache",
175 "relationsFromThisTaxon.toTaxon.sources.citation.sources.idNamespace",
176 "relationsFromThisTaxon.toTaxon.sources.citation.sources.idInSource",
178 "relationsToThisTaxon.type.$",
179 "relationsToThisTaxon.fromTaxon.name.rank.titleCache",
180 "relationsToThisTaxon.fromTaxon.sec.updated",
181 "relationsToThisTaxon.fromTaxon.sec.titleCache",
182 "relationsToThisTaxon.fromTaxon.sources.citation.sources.idNamespace",
183 "relationsToThisTaxon.fromTaxon.sources.citation.sources.idInSource",
186 "taxonNodes.classification" });
188 /** Hibernate taxon node initialisation strategy */
189 private static final List
<String
> TAXON_NODE_INIT_STRATEGY
= Arrays
.asList(new String
[] {
193 "classification.reference.$",
194 "classification.reference.authorTeam.$" });
196 /** Hibernate classification vocabulary initialisation strategy */
197 private static final List
<String
> VOC_CLASSIFICATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
199 "classification.reference.$",
200 "classification.reference.authorTeam.$" });
202 /** Hibernate classification vocabulary initialisation strategy */
203 private static final List
<String
> COMMON_INIT_STRATEGY
= Arrays
.asList(new String
[] {});
205 public NameCatalogueController() {
207 setInitializationStrategy(Arrays
.asList(new String
[] { "$" }));
214 * eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu
215 * .etaxonomy.cdm.api.service.IService)
219 public void setService(INameService service
) {
220 this.service
= service
;
224 * Returns a documentation page for the Name Search API.
226 * URI: <b>/{datasource-name}/name_catalogue</b>
230 * @return Html page describing the Name Search API
231 * @throws IOException
233 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {})
234 public ModelAndView
doGetNameSearchDocumentation(
235 HttpServletRequest request
, HttpServletResponse response
)
237 ModelAndView mv
= new ModelAndView();
238 // Read apt documentation file.
239 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-default.apt");
240 // using input stream as this works for both files in the classes directory
241 // as well as files inside jars
242 InputStream aptInputStream
= resource
.getInputStream();
244 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
245 // Convert Apt to Html
246 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
247 mv
.addAllObjects(modelMap
);
249 HtmlView hv
= new HtmlView();
255 * Returns a list of scientific names matching the <code>{query}</code>
256 * string pattern. Each of these scientific names is accompanied by a list of
257 * name uuids, a list of taxon uuids, a list of accepted taxon uuids, etc.
259 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
261 * URI: <b>/{datasource-name}/name_catalogue</b>
264 * The base scientific name pattern(s) to query for. The query can
265 * contain wildcard characters ('*'). The query can be
266 * performed with no wildcard or with the wildcard at the
267 * begin and / or end depending on the search pattern.
268 * @param request Http servlet request.
269 * @param response Http servlet response.
270 * @return a list of {@link NameSearch} objects each corresponding to a
271 * single query. These are built from {@link TaxonNameBase}
272 * entities which are in turn initialized using
273 * the {@link #NAME_SEARCH_INIT_STRATEGY}
274 * @throws IOException
276 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {"query"})
277 public ModelAndView
doGetNameSearch(@RequestParam(value
= "query", required
= true) String
[] queries
,
278 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
279 return doGetNameSearch(queries
, DEFAULT_SEARCH_TYPE
, request
, response
);
283 * Returns a list of scientific names matching the <code>{query}</code>
284 * string pattern. Each of these scientific names is accompanied by a list of
285 * name uuids, a list of taxon uuids and a list of accepted taxon uuids.
287 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
289 * URI: <b>/{datasource-name}/name_catalogue</b>
292 * The scientific name pattern(s) to query for. The query can
293 * contain wildcard characters ('*'). The query can be
294 * performed with no wildcard or with the wildcard at the
295 * begin and / or end depending on the search pattern.
297 * The type of name to query. This be either
298 * "name" : scientific name corresponding to 'name cache' in CDM or
299 * "title" : complete name corresponding to 'title cache' in CDM
300 * @param request Http servlet request.
301 * @param response Http servlet response.
302 * @return a List of {@link NameSearch} objects each corresponding to a
303 * single query. These are built from {@link TaxonNameBase} entities
304 * which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
305 * @throws IOException
307 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {"query", "type"})
308 public ModelAndView
doGetNameSearch(@RequestParam(value
= "query", required
= true) String
[] queries
,
309 @RequestParam(value
= "type", required
= false, defaultValue
= DEFAULT_SEARCH_TYPE
) String searchType
,
310 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
311 ModelAndView mv
= new ModelAndView();
312 List
<RemoteResponse
> nsList
= new ArrayList
<RemoteResponse
>();
314 // if search type is not known then return error
315 if (!searchType
.equals(NAME_SEARCH
) && !searchType
.equals(TITLE_SEARCH
)) {
316 ErrorResponse er
= new ErrorResponse();
317 er
.setErrorMessage("searchType parameter can only be set as" + NAME_SEARCH
+ " or "
323 // search through each query
324 for (String query
: queries
) {
326 String queryWOWildcards
= getQueryWithoutWildCards(query
);
327 MatchMode mm
= getMatchModeFromQuery(query
);
328 logger
.info("doGetNameSearch()" + request
.getServletPath() + " for query \"" + query
329 + "\" without wild cards : " + queryWOWildcards
+ " and match mode : " + mm
);
330 List
<NonViralName
> nameList
= new ArrayList
<NonViralName
>();
332 // if "name" search then find by name cache
333 if (searchType
.equals(NAME_SEARCH
)) {
334 nameList
= (List
<NonViralName
>) service
.findNamesByNameCache(queryWOWildcards
, mm
,
335 NAME_SEARCH_INIT_STRATEGY
);
338 //if "title" search then find by title cache
339 if (searchType
.equals(TITLE_SEARCH
)) {
340 nameList
= (List
<NonViralName
>) service
.findNamesByTitleCache(queryWOWildcards
, mm
,
341 NAME_SEARCH_INIT_STRATEGY
);
344 // if search is successful then get related information , else return error
345 if (nameList
== null || !nameList
.isEmpty()) {
346 NameSearch ns
= new NameSearch();
347 ns
.setRequest(query
);
349 for (NonViralName nvn
: nameList
) {
350 // we need to retrieve both taxon uuid of name queried and
351 // the corresponding accepted taxa.
352 // reason to return accepted taxa also, is to be able to get from
353 // scientific name to taxon concept in two web service calls.
354 Set
<TaxonBase
> tbSet
= nvn
.getTaxonBases();
355 Set
<TaxonBase
> accTbSet
= new HashSet
<TaxonBase
>();
356 for (TaxonBase tb
: tbSet
) {
357 // if synonym then get accepted taxa.
358 if (tb
instanceof Synonym
) {
359 Synonym synonym
= (Synonym
) tb
;
360 Set
<SynonymRelationship
> synRelationships
= synonym
.getSynonymRelations();
361 for (SynonymRelationship sr
: synRelationships
) {
362 Taxon accTaxon
= sr
.getAcceptedTaxon();
363 accTbSet
.add(accTaxon
);
369 // update name search object
370 ns
.addToResponseList(nvn
.getTitleCache(), nvn
.getNameCache(), nvn
.getUuid()
371 .toString(), tbSet
, accTbSet
);
376 ErrorResponse er
= new ErrorResponse();
377 er
.setErrorMessage("No Taxon Name for given query : " + query
);
382 mv
.addObject(nsList
);
388 * Returns a documentation page for the Name Information API.
390 * URI: <b>/{datasource-name}/name_catalogue/name</b>
392 * @param request Http servlet request.
393 * @param response Http servlet response.
394 * @return Html page describing the Name Information API
395 * @throws IOException
397 @RequestMapping(value
= { "name" }, method
= RequestMethod
.GET
, params
= {})
398 public ModelAndView
doGetNameInformationDocumentation(
399 HttpServletRequest request
, HttpServletResponse response
)
401 ModelAndView mv
= new ModelAndView();
402 // Read apt documentation file.
403 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-name-info.apt");
404 // using input stream as this works for both files in the classes directory
405 // as well as files inside jars
406 InputStream aptInputStream
= resource
.getInputStream();
408 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
409 // Convert Apt to Html
410 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
411 mv
.addAllObjects(modelMap
);
413 HtmlView hv
= new HtmlView();
419 * Returns information related to the scientific name matching the given
420 * <code>{nameUuid}</code>. The information includes the name string,
421 * relationships, rank, list of related lsids / taxon uuids, etc.
423 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-name-info.html">here</a>
425 * URI: <b>/{datasource-name}/name_catalogue/name</b>
427 * @param nameUuids uuid(s) of the scientific name to search for.
428 * @param request Http servlet request.
429 * @param response Http servlet response.
430 * @return a List of {@link NameInformation} objects each corresponding to a
431 * single name uuid. These are built from {@link TaxonNameBase} entities
432 * which are in turn initialized using the {@link #NAME_INFORMATION_INIT_STRATEGY}
433 * @throws IOException
435 @RequestMapping(value
= { "name" }, method
= RequestMethod
.GET
, params
= {"nameUuid"})
436 public ModelAndView
doGetNameInformation(@RequestParam(value
= "nameUuid", required
= true) String
[] nameUuids
,
437 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
438 ModelAndView mv
= new ModelAndView();
439 List
<RemoteResponse
> niList
= new ArrayList
<RemoteResponse
>();
440 // loop through each name uuid
441 for (String nameUuid
: nameUuids
) {
442 logger
.info("doGetNameInformation()" + request
.getServletPath() + " for name uuid \""
445 NonViralName nvn
= (NonViralName
) service
.findNameByUuid(UUID
.fromString(nameUuid
),
446 NAME_INFORMATION_INIT_STRATEGY
);
448 // if search is successful then get related information, else return error
450 NameInformation ni
= new NameInformation();
451 ni
.setRequest(nameUuid
);
452 Reference ref
= (Reference
) nvn
.getNomenclaturalReference();
453 String citation
= "";
454 String citation_details
= "";
456 citation
= ref
.getTitleCache();
458 // update name information object
459 ni
.setResponse(nvn
.getTitleCache(), nvn
.getNameCache(), nvn
.getRank().getTitleCache(),
460 nvn
.getStatus(), citation
, nvn
.getRelationsFromThisName(),
461 nvn
.getRelationsToThisName(), nvn
.getTaxonBases());
464 ErrorResponse re
= new ErrorResponse();
466 if(isValid(nameUuid
)) {
467 re
.setErrorMessage("No Name for given UUID : " + nameUuid
);
469 re
.setErrorMessage(nameUuid
+ " not a valid UUID");
474 mv
.addObject(niList
);
479 * Returns a documentation page for the Taxon Information API.
481 * URI: <b>/{datasource-name}/name_catalogue/taxon</b>
483 * @param request Http servlet request.
484 * @param response Http servlet response.
485 * @return Html page describing the Taxon Information API
486 * @throws IOException
488 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
, params
= {})
489 public ModelAndView
doGetTaxonInformation(
490 HttpServletRequest request
, HttpServletResponse response
)
492 ModelAndView mv
= new ModelAndView();
493 // Read apt documentation file.
494 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-taxon-info.apt");
495 // using input stream as this works for both files in the classes directory
496 // as well as files inside jars
497 InputStream aptInputStream
= resource
.getInputStream();
499 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
500 // Convert Apt to Html
501 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
502 mv
.addAllObjects(modelMap
);
504 HtmlView hv
= new HtmlView();
510 * Returns information related to the taxon matching the given
511 * <code>{taxonUuid}</code>. The information includes the name cache, title cache
512 * relationship type, taxonomic status, information , etc.
514 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
516 * URI: <b>/{datasource-name}/name_catalogue</b>
519 * The taxon uuid to query for. The classification returned corresponds
520 * to the first in the alphabetically sorted list of classifications
521 * currently available in the database.
523 * @param request Http servlet request.
524 * @param response Http servlet response.
525 * @return a List of {@link TaxonInformation} objects each corresponding to a
526 * single query. These are built from {@TaxonBase} entities which are
527 * in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
528 * @throws IOException
530 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
,params
= {"taxonUuid"})
531 public ModelAndView
doGetTaxonInformation(
532 @RequestParam(value
= "taxonUuid", required
= true) String
[] taxonUuids
,
533 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
534 return doGetTaxonInformation(taxonUuids
,CLASSIFICATION_DEFAULT
, request
, response
);
538 * Returns information related to the taxon matching the given
539 * <code>{taxonUuid}</code>. The information includes the name cache, title cache
540 * relationship type, taxonomic status, information , etc.
542 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
544 * URI: <b>/{datasource-name}/name_catalogue/taxon</b>
547 * The taxon uuid to query for.
548 * @param classification
549 * [Optional] String representing the taxonomic classification to use for
550 * building the classification tree. Defaults to the first in the alphabetically
551 * sorted list of classifications currently available in the database.
553 * @param request Http servlet request.
554 * @param response Http servlet response.
555 * @return a List of {@link TaxonInformation} objects each corresponding to a
556 * single query. These are built from {@TaxonBase} entities which are
557 * in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
558 * @throws IOException
560 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
, params
= {"taxonUuid", "classification"})
561 public ModelAndView
doGetTaxonInformation(
562 @RequestParam(value
= "taxonUuid", required
= true) String
[] taxonUuids
,
563 @RequestParam(value
= "classification", required
= false, defaultValue
= CLASSIFICATION_DEFAULT
) String classificationType
,
564 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
565 ModelAndView mv
= new ModelAndView();
566 List
<RemoteResponse
> tiList
= new ArrayList
<RemoteResponse
>();
567 // loop through each name uuid
568 for (String taxonUuid
: taxonUuids
) {
569 logger
.info("doGetTaxonInformation()" + request
.getServletPath() + " for taxon uuid \""
572 TaxonBase tb
= taxonService
.findTaxonByUuid(UUID
.fromString(taxonUuid
),
573 TAXON_INFORMATION_INIT_STRATEGY
);
575 // if search is successful then get related information, else return error
577 TaxonInformation ti
= new TaxonInformation();
578 ti
.setRequest(taxonUuid
);
579 // check if result (taxon base) is a taxon or synonym
580 if (tb
.isInstanceOf(Taxon
.class)) {
581 Taxon taxon
= (Taxon
) tb
;
582 // build classification map
583 Map classificationMap
= getClassification(taxon
, classificationType
);
585 logger
.info("taxon uuid " + taxon
.getUuid().toString() + " original hash code : " + System
.identityHashCode(taxon
) + ", name class " + taxon
.getName().getClass().getName());
586 // update taxon information object with taxon related data
587 NonViralName nvn
= (NonViralName
) taxon
.getName();
589 String secTitle
= "" ;
590 String modified
= "";
591 if(taxon
.getSec() != null) {
592 secTitle
= taxon
.getSec().getTitleCache();
593 DateTime dt
= taxon
.getUpdated();
594 modified
= fmt
.print(dt
);
597 Set
<IdentifiableSource
> sources
= taxon
.getSources();
598 String
[] didname
= getDatasetIdName(sources
);
600 ti
.setResponseTaxon(tb
.getTitleCache(),
602 nvn
.getRank().getTitleCache(),
603 ACCEPTED_NAME_STATUS
,
613 Set
<SynonymRelationship
> synRelationships
= taxon
.getSynonymRelations();
614 // add synonyms (if exists) to taxon information object
615 for (SynonymRelationship sr
: synRelationships
) {
616 Synonym syn
= sr
.getSynonym();
617 String uuid
= syn
.getUuid().toString();
618 String title
= syn
.getTitleCache();
619 TaxonNameBase synnvn
= (TaxonNameBase
) syn
.getName();
620 String name
= synnvn
.getTitleCache();
621 String rank
= synnvn
.getRank().getTitleCache();
622 String status
= SYNONYM_STATUS
;
623 String relLabel
= sr
.getType()
624 .getInverseRepresentation(Language
.DEFAULT())
629 if(syn
.getSec() != null) {
630 secTitle
= syn
.getSec().getTitleCache();
631 DateTime dt
= syn
.getUpdated();
632 modified
= fmt
.print(dt
);
635 sources
= syn
.getSources();
636 didname
= getDatasetIdName(sources
);
638 ti
.addToResponseRelatedTaxa(uuid
,
651 // build relationship information as,
652 // - relationships from the requested taxon
653 Set
<TaxonRelationship
> trFromSet
= taxon
.getRelationsFromThisTaxon();
654 for (TaxonRelationship tr
: trFromSet
) {
655 String titleTo
= tr
.getToTaxon().getTitleCache();
656 TaxonNameBase tonvn
= (TaxonNameBase
) tr
.getToTaxon().getName();
657 String name
= tonvn
.getTitleCache();
658 String rank
= tonvn
.getRank().getTitleCache();
659 String uuid
= tr
.getToTaxon().getUuid().toString();
660 String status
= ACCEPTED_NAME_STATUS
;
661 String relLabel
= tr
.getType().getRepresentation(Language
.DEFAULT())
666 if(tr
.getToTaxon().getSec() != null) {
667 secTitle
= tr
.getToTaxon().getSec().getTitleCache();
668 DateTime dt
= tr
.getToTaxon().getUpdated();
669 modified
= fmt
.print(dt
);
672 sources
= tr
.getToTaxon().getSources();
673 didname
= getDatasetIdName(sources
);
675 ti
.addToResponseRelatedTaxa(uuid
,
686 //logger.info("titleTo : " + titleTo + " , name : " + name);
689 // - relationships to the requested taxon
690 Set
<TaxonRelationship
> trToSet
= taxon
.getRelationsToThisTaxon();
691 for (TaxonRelationship tr
: trToSet
) {
692 String titleFrom
= tr
.getFromTaxon().getTitleCache();
693 TaxonNameBase fromnvn
= (TaxonNameBase
) tr
.getFromTaxon().getName();
694 String name
= fromnvn
.getTitleCache();
695 String rank
= fromnvn
.getRank().getTitleCache();
696 String uuid
= tr
.getFromTaxon().getUuid().toString();
697 String status
= ACCEPTED_NAME_STATUS
;
698 String relLabel
= tr
.getType()
699 .getInverseRepresentation(Language
.DEFAULT())
702 if(tr
.getFromTaxon().getSec() != null) {
703 secTitle
= tr
.getFromTaxon().getSec().getTitleCache();
704 DateTime dt
= tr
.getFromTaxon().getSec().getUpdated();
705 modified
= fmt
.print(dt
);
708 sources
= tr
.getFromTaxon().getSources();
709 didname
= getDatasetIdName(sources
);
711 secTitle
= (tr
.getFromTaxon().getSec() == null) ?
"" : tr
.getFromTaxon().getSec().getTitleCache();
712 ti
.addToResponseRelatedTaxa(uuid
,
723 //logger.info("titleFrom : " + titleFrom + " , name : " + name);
725 } else if (tb
instanceof Synonym
) {
726 Synonym synonym
= (Synonym
) tb
;
727 TaxonNameBase nvn
= (TaxonNameBase
) synonym
.getName();
728 // update taxon information object with synonym related data
729 DateTime dt
= synonym
.getUpdated();
730 String modified
= fmt
.print(dt
);
732 Set
<IdentifiableSource
> sources
= synonym
.getSources();
733 String
[] didname
= getDatasetIdName(sources
);
735 String secTitle
= (synonym
.getSec() == null) ?
"" : synonym
.getSec().getTitleCache();
736 ti
.setResponseTaxon(synonym
.getTitleCache(),
738 nvn
.getRank().getTitleCache(),
740 buildFlagMap(synonym
),
741 new TreeMap
<String
,Map
>(),
747 // add accepted taxa (if exists) to taxon information object
749 Set
<SynonymRelationship
> synRelationships
= synonym
.getSynonymRelations();
750 for (SynonymRelationship sr
: synRelationships
) {
751 Taxon accTaxon
= sr
.getAcceptedTaxon();
752 String uuid
= accTaxon
.getUuid().toString();
753 logger
.info("acc taxon uuid " + accTaxon
.getUuid().toString() + " original hash code : " + System
.identityHashCode(accTaxon
) + ", name class " + accTaxon
.getName().getClass().getName());
754 String title
= accTaxon
.getTitleCache();
755 logger
.info("taxon title cache : " + accTaxon
.getTitleCache());
757 TaxonNameBase accnvn
= (TaxonNameBase
)accTaxon
.getName();
758 String name
= accnvn
.getTitleCache();
759 String rank
= accnvn
.getRank().getTitleCache();
760 String status
= ACCEPTED_NAME_STATUS
;
761 String relLabel
= sr
.getType().getRepresentation(Language
.DEFAULT())
763 dt
= accTaxon
.getUpdated();
764 modified
= fmt
.print(dt
);
766 sources
= accTaxon
.getSources();
767 didname
= getDatasetIdName(sources
);
769 secTitle
= (accTaxon
.getSec() == null) ?
"" : accTaxon
.getSec().getTitleCache();
770 ti
.addToResponseRelatedTaxa(uuid
,
786 ErrorResponse re
= new ErrorResponse();
787 if(isValid(taxonUuid
)) {
788 re
.setErrorMessage("No Taxon for given UUID : " + taxonUuid
);
790 re
.setErrorMessage(taxonUuid
+ " not a valid UUID");
795 mv
.addObject(tiList
);
800 * Returns a list of all available classifications (with associated referenc information) and the default classification.
802 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-classification-info.html">here</a>
804 * URI: <b>/{datasource-name}/name_catalogue/voc/classification</b>
806 * @param request Http servlet request.
807 * @param response Http servlet response.
808 * @return a List of {@link Classification} objects represebted as strings.
809 * These are initialized using the {@link #VOC_CLASSIFICATION_INIT_STRATEGY}
810 * @throws IOException
812 @RequestMapping(value
= { "voc/classification" }, method
= RequestMethod
.GET
, params
= {})
813 public ModelAndView
doGetClassificationMap(HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
814 List
<Map
> cmapList
= new ArrayList
<Map
>();
815 Map
<String
, String
> classifications
= new HashMap
<String
, String
>();
816 ModelAndView mv
= new ModelAndView();
817 List
<Classification
> clist
= getClassificationList(100);
818 boolean isFirst
= true;
819 Iterator itr
= clist
.iterator();
820 // loop through all classifications and populate map with
821 // (classificationKey, reference) elements
822 while(itr
.hasNext()) {
823 Classification c
= (Classification
) itr
.next();
824 String refTitleCache
= "";
825 String classificationKey
= removeInternalWhitespace(c
.getTitleCache());
826 if(c
.getReference() != null) {
827 refTitleCache
= c
.getReference().getTitleCache();
829 // default is the first element of the list
830 // always created with the same sorting (DESCENDING)
832 Map
<String
, String
> defaultMap
= new HashMap
<String
, String
>();
833 defaultMap
.put("default", classificationKey
);
834 cmapList
.add(defaultMap
);
837 classifications
.put(classificationKey
, refTitleCache
);
840 Map
<String
, Map
> cmap
= new HashMap
<String
, Map
>();
841 cmap
.put("classification",classifications
);
843 mv
.addObject(cmapList
);
848 * Returns the Dataset ID / Name of the given original source.
849 * FIXME: Very hacky and needs to be revisited. Mainly for deciding on which objects to use during import.
850 * FIXME: dataset id is mapped to a DWC term - is that right?
852 * @param sources Set of sources attached to a taxa / synonym
855 * @return String array where [0] is the datsetID and [1] is the datsetName
857 private String
[] getDatasetIdName(Set
<IdentifiableSource
> sources
) {
858 String didname
[] = {"",""};
859 Iterator
<IdentifiableSource
> itr
= sources
.iterator();
860 while(itr
.hasNext()) {
861 IdentifiableSource source
= itr
.next();
862 Reference ref
= source
.getCitation();
863 Set
<IdentifiableSource
> ref_sources
= ref
.getSources();
864 Iterator
<IdentifiableSource
> ref_itr
= ref_sources
.iterator();
865 while(ref_itr
.hasNext()) {
866 IdentifiableSource ref_source
= ref_itr
.next();
867 if(ref_source
.getIdNamespace().equals(DWC_DATASET_ID
)) {
868 didname
[0] = ref_source
.getIdInSource();
872 if(!didname
[0].isEmpty()) {
873 didname
[1] = ref
.getTitleCache();
881 * Returns the match mode by parsing the input string of wildcards.
886 * @return {@link MatchMode} depending on the the position of the wildcard (*)
888 private MatchMode
getMatchModeFromQuery(String query
) {
889 if (query
.startsWith("*") && query
.endsWith("*")) {
890 return MatchMode
.ANYWHERE
;
891 } else if (query
.startsWith("*")) {
892 return MatchMode
.END
;
893 } else if (query
.endsWith("*")) {
894 return MatchMode
.BEGINNING
;
896 return MatchMode
.EXACT
;
901 * Removes wildcards from the input string.
906 * @return input string with wildcards removed
908 private String
getQueryWithoutWildCards(String query
) {
910 String newQuery
= query
;
912 if (query
.startsWith("*")) {
913 newQuery
= newQuery
.substring(1, newQuery
.length());
916 if (query
.endsWith("*")) {
917 newQuery
= newQuery
.substring(0, newQuery
.length() - 1);
920 return newQuery
.trim();
924 * Build map with taxon flag key-value pairs.
926 private Map
<String
, String
> buildFlagMap(TaxonBase tb
) {
927 Map
<String
, String
> flags
= new Hashtable
<String
, String
>();
928 flags
.put(DOUBTFUL_FLAG
, Boolean
.toString(tb
.isDoubtful()));
933 * Build classification map.
935 private Map
<String
, Map
> getClassification(Taxon taxon
, String classificationType
) {
936 // Using TreeMap is important, because we need the sorting of the classification keys
937 // in the map to be stable.
938 TreeMap
<String
, Map
> sourceClassificationMap
= buildClassificationMap(taxon
, classificationType
);
940 // if classification key is 'default' then return the default element of the map
941 if(classificationType
.equals(CLASSIFICATION_DEFAULT
) && !sourceClassificationMap
.isEmpty()) {
942 List
<Classification
> clist
= getClassificationList(1);
943 String defaultKey
= removeInternalWhitespace(clist
.get(0).getTitleCache());
944 return sourceClassificationMap
.get(defaultKey
);
945 // if classification key is provided then return the classification corresponding to the key
946 } else if(sourceClassificationMap
.containsKey(classificationType
)) {
947 return sourceClassificationMap
.get(classificationType
);
948 // if classification key is 'all' then return the entire map
949 } else if(classificationType
.equals(CLASSIFICATION_ALL
)) {
950 return sourceClassificationMap
;
952 return new TreeMap
<String
,Map
>();
957 * Build classification map.
959 private TreeMap
<String
, Map
> buildClassificationMap(Taxon taxon
, String classificationType
) {
960 // Using TreeMap is important, because we need the sorting of the classification keys
961 // in the map to be stable.
962 TreeMap
<String
, Map
> sourceClassificationMap
= new TreeMap
<String
, Map
>();
963 Set
<TaxonNode
> taxonNodes
= taxon
.getTaxonNodes();
964 //loop through taxon nodes and build classification map for each classification key
965 for (TaxonNode tn
: taxonNodes
) {
966 Map
<String
, String
> classificationMap
= new LinkedHashMap
<String
, String
>();
967 List
<TaxonNode
> tnList
= classificationService
.loadTreeBranchToTaxon(taxon
,
968 tn
.getClassification(), null, TAXON_NODE_INIT_STRATEGY
);
969 for (TaxonNode classificationtn
: tnList
) {
970 classificationMap
.put(classificationtn
.getTaxon().getName().getRank().getTitleCache(),
971 classificationtn
.getTaxon().getName().getTitleCache());
973 String cname
= removeInternalWhitespace(tn
.getClassification().getTitleCache());
974 logger
.info("Building classification map " + cname
);
975 sourceClassificationMap
.put(cname
, classificationMap
);
977 return sourceClassificationMap
;
980 private String
removeInternalWhitespace(String withWSpace
) {
981 String
[] words
= withWSpace
.split("\\s+");
982 // "\\s+" in regular expression language meaning one or
984 StringBuilder builder
= new StringBuilder();
985 for (String word
: words
) {
986 builder
.append(word
);
988 return builder
.toString();
991 private List
<Classification
> getClassificationList(int limit
) {
992 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
993 orderHints
.add(new OrderHint("titleCache", SortOrder
.DESCENDING
));
994 List
<Classification
> clist
= classificationService
.listClassifications(limit
, 0, orderHints
, VOC_CLASSIFICATION_INIT_STRATEGY
);
998 private boolean isValid(String uuid
){
999 if( uuid
== null) return false;
1001 // we have to convert to object and back to string because the built in fromString does not have
1002 // good validation logic.
1004 UUID fromStringUUID
= UUID
.fromString(uuid
);
1005 String toStringUUID
= fromStringUUID
.toString();
1007 System
.out
.println("input uuid : " + uuid
+ " , parsed uuid : " + toStringUUID
);
1008 return toStringUUID
.equals(uuid
);
1009 } catch(IllegalArgumentException e
) {
1014 public void setResourceLoader(ResourceLoader resourceLoader
) {
1015 this.resourceLoader
= resourceLoader
;