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
.springframework
.beans
.factory
.annotation
.Autowired
;
26 import org
.springframework
.context
.ResourceLoaderAware
;
27 import org
.springframework
.core
.io
.Resource
;
28 import org
.springframework
.core
.io
.ResourceLoader
;
29 import org
.springframework
.stereotype
.Controller
;
30 import org
.springframework
.web
.bind
.annotation
.RequestMapping
;
31 import org
.springframework
.web
.bind
.annotation
.RequestMethod
;
32 import org
.springframework
.web
.bind
.annotation
.RequestParam
;
33 import org
.springframework
.web
.servlet
.ModelAndView
;
35 import eu
.etaxonomy
.cdm
.api
.service
.IClassificationService
;
36 import eu
.etaxonomy
.cdm
.api
.service
.INameService
;
37 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonService
;
38 import eu
.etaxonomy
.cdm
.common
.DocUtils
;
40 import eu
.etaxonomy
.cdm
.remote
.dto
.common
.ErrorResponse
;
41 import eu
.etaxonomy
.cdm
.remote
.dto
.common
.RemoteResponse
;
42 import eu
.etaxonomy
.cdm
.remote
.dto
.namecatalogue
.NameInformation
;
43 import eu
.etaxonomy
.cdm
.remote
.dto
.namecatalogue
.NameSearch
;
44 import eu
.etaxonomy
.cdm
.remote
.dto
.namecatalogue
.TaxonInformation
;
45 import eu
.etaxonomy
.cdm
.remote
.view
.HtmlView
;
46 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
47 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
48 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
49 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
50 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
51 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
52 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
53 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
54 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
55 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
56 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
57 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
58 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
59 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
60 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
63 * The controller class for the namespace 'name_catalogue'. This web service namespace
64 * is an add-on to the already existing CDM REST API and provides information relating
65 * to scientific names as well as taxa present in the underlying datasource.
69 * @created 15-Apr-2012
73 @RequestMapping(value
= { "/name_catalogue" })
74 public class NameCatalogueController
extends BaseController
<TaxonNameBase
, INameService
> implements ResourceLoaderAware
{
76 private ResourceLoader resourceLoader
;
78 /** Taxonomic status 'accepted' string */
79 public static final String ACCEPTED_NAME_STATUS
= "accepted";
81 /** Taxonpmic status 'synonym' string */
82 public static final String SYNONYM_STATUS
= "synonym";
84 /** Flag 'doubtful' strings */
85 public static final String DOUBTFUL_FLAG
= "doubtful";
87 /** Base scientific name search type */
88 public static final String NAME_SEARCH
= "name";
90 /** Complete scientific name search type */
91 public static final String TITLE_SEARCH
= "title";
93 /** Default name search type */
94 public static final String DEFAULT_SEARCH_TYPE
= NAME_SEARCH
;
96 /** Classifcation 'default' key */
97 public static final String CLASSIFICATION_DEFAULT
= "default";
99 /** Classifcation 'all' key */
100 public static final String CLASSIFICATION_ALL
= "all";
103 private ITaxonService taxonService
;
106 private IClassificationService classificationService
;
108 /** Hibernate name search initialisation strategy */
109 private static final List
<String
> NAME_SEARCH_INIT_STRATEGY
= Arrays
.asList(new String
[] {
110 "combinationAuthorTeam.$",
111 "exCombinationAuthorTeam.$",
112 "basionymAuthorTeam.$",
113 "exBasionymAuthorTeam.$",
116 "taxonBases.synonymRelations.type.$"});
118 /** Hibernate name information initialisation strategy */
119 private static final List
<String
> NAME_INFORMATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
122 "nomenclaturalReference.$",
123 "combinationAuthorTeam.$",
124 "exCombinationAuthorTeam.$",
125 "basionymAuthorTeam.$",
126 "exBasionymAuthorTeam.$",
127 "relationsToThisName.fromName.$",
128 "relationsToThisName.nomenclaturalReference.$",
129 "relationsToThisName.type.$",
130 "relationsFromThisName.toName.$",
131 "relationsFromThisName.nomenclaturalReference.$",
132 "relationsFromThisName.type.$"});
134 /** Hibernate taxon information initialisation strategy */
135 private static final List
<String
> TAXON_INFORMATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
137 "name.rank.titleCache",
138 "synonymRelations.synonym.name.rank.titleCache",
139 "synonymRelations.acceptedTaxon.name.rank.titleCache",
140 "synonymRelations.type.$",
141 "taxonRelations.toTaxon.$",
142 "taxonRelations.fromTaxon.$",
143 "taxonRelations.type.$",
144 "synonymRelations.relatedTo.name.rank.titleCache",
145 "relationsFromThisTaxon.type.$",
146 "relationsFromThisTaxon.relatedTo.name.rank.titleCache",
147 "relationsToThisTaxon.type.$",
148 "relationsToThisTaxon.relatedFrom.name.rank.titleCache",
150 "taxonNodes.classification" });
152 /** Hibernate taxon node initialisation strategy */
153 private static final List
<String
> TAXON_NODE_INIT_STRATEGY
= Arrays
.asList(new String
[] {
157 "classification.reference.$",
158 "classification.reference.authorTeam.$" });
160 /** Hibernate classification vocabulary initialisation strategy */
161 private static final List
<String
> VOC_CLASSIFICATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
163 "classification.reference.$",
164 "classification.reference.authorTeam.$" });
165 public NameCatalogueController() {
167 setInitializationStrategy(Arrays
.asList(new String
[] { "$" }));
174 * eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu
175 * .etaxonomy.cdm.api.service.IService)
179 public void setService(INameService service
) {
180 this.service
= service
;
184 * Returns a documentation page for the Name Search API.
186 * URI: <b>/{datasource-name}/name_catalogue</b>
190 * @return Html page describing the Name Search API
191 * @throws IOException
193 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {})
194 public ModelAndView
doGetNameSearchDocumentation(
195 HttpServletRequest request
, HttpServletResponse response
)
197 ModelAndView mv
= new ModelAndView();
198 // Read apt documentation file.
199 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-default.apt");
200 // using input stream as this works for both files in the classes directory
201 // as well as files inside jars
202 InputStream aptInputStream
= resource
.getInputStream();
204 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
205 // Convert Apt to Html
206 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
207 mv
.addAllObjects(modelMap
);
209 HtmlView hv
= new HtmlView();
215 * Returns a list of scientific names matching the <code>{query}</code>
216 * string pattern. Each of these scientific names is accompanied by a list of
217 * name uuids, a list of taxon uuids, a list of accepted taxon uuids, etc.
219 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
221 * URI: <b>/{datasource-name}/name_catalogue</b>
224 * The base scientific name pattern(s) to query for. The query can
225 * contain wildcard characters ('*'). The query can be
226 * performed with no wildcard or with the wildcard at the
227 * begin and / or end depending on the search pattern.
228 * @param request Http servlet request.
229 * @param response Http servlet response.
230 * @return a list of {@link NameSearch} objects each corresponding to a
231 * single query. These are built from {@link TaxonNameBase}
232 * entities which are in turn initialized using
233 * the {@link #NAME_SEARCH_INIT_STRATEGY}
234 * @throws IOException
236 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {"query"})
237 public ModelAndView
doGetNameSearch(@RequestParam(value
= "query", required
= true) String
[] queries
,
238 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
239 return doGetNameSearch(queries
, DEFAULT_SEARCH_TYPE
, request
, response
);
243 * Returns a list of scientific names matching the <code>{query}</code>
244 * string pattern. Each of these scientific names is accompanied by a list of
245 * name uuids, a list of taxon uuids and a list of accepted taxon uuids.
247 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
249 * URI: <b>/{datasource-name}/name_catalogue</b>
252 * The scientific name pattern(s) to query for. The query can
253 * contain wildcard characters ('*'). The query can be
254 * performed with no wildcard or with the wildcard at the
255 * begin and / or end depending on the search pattern.
257 * The type of name to query. This be either
258 * "name" : scientific name corresponding to 'name cache' in CDM or
259 * "title" : complete name corresponding to 'title cache' in CDM
260 * @param request Http servlet request.
261 * @param response Http servlet response.
262 * @return a List of {@link NameSearch} objects each corresponding to a
263 * single query. These are built from {@link TaxonNameBase} entities
264 * which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
265 * @throws IOException
267 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {"query", "type"})
268 public ModelAndView
doGetNameSearch(@RequestParam(value
= "query", required
= true) String
[] queries
,
269 @RequestParam(value
= "type", required
= false, defaultValue
= DEFAULT_SEARCH_TYPE
) String searchType
,
270 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
271 ModelAndView mv
= new ModelAndView();
272 List
<RemoteResponse
> nsList
= new ArrayList
<RemoteResponse
>();
274 // if search type is not known then return error
275 if (!searchType
.equals(NAME_SEARCH
) && !searchType
.equals(TITLE_SEARCH
)) {
276 ErrorResponse er
= new ErrorResponse();
277 er
.setErrorMessage("searchType parameter can only be set as" + NAME_SEARCH
+ " or "
283 // search through each query
284 for (String query
: queries
) {
286 String queryWOWildcards
= getQueryWithoutWildCards(query
);
287 MatchMode mm
= getMatchModeFromQuery(query
);
288 logger
.info("doGetNameSearch()" + request
.getServletPath() + " for query \"" + query
289 + "\" without wild cards : " + queryWOWildcards
+ " and match mode : " + mm
);
290 List
<NonViralName
> nameList
= new ArrayList
<NonViralName
>();
292 // if "name" search then find by name cache
293 if (searchType
.equals(NAME_SEARCH
)) {
294 nameList
= (List
<NonViralName
>) service
.findNamesByNameCache(queryWOWildcards
, mm
,
295 NAME_SEARCH_INIT_STRATEGY
);
298 //if "title" search then find by title cache
299 if (searchType
.equals(TITLE_SEARCH
)) {
300 nameList
= (List
<NonViralName
>) service
.findNamesByTitleCache(queryWOWildcards
, mm
,
301 NAME_SEARCH_INIT_STRATEGY
);
304 // if search is successful then get related information , else return error
305 if (nameList
== null || !nameList
.isEmpty()) {
306 NameSearch ns
= new NameSearch();
307 ns
.setRequest(query
);
309 for (NonViralName nvn
: nameList
) {
310 // we need to retrieve both taxon uuid of name queried and
311 // the corresponding accepted taxa.
312 // reason to return accepted taxa also, is to be able to get from
313 // scientific name to taxon concept in two web service calls.
314 Set
<TaxonBase
> tbSet
= nvn
.getTaxonBases();
315 Set
<TaxonBase
> accTbSet
= new HashSet
<TaxonBase
>();
316 for (TaxonBase tb
: tbSet
) {
317 // if synonym then get accepted taxa.
318 if (tb
instanceof Synonym
) {
319 Synonym synonym
= (Synonym
) tb
;
320 Set
<SynonymRelationship
> synRelationships
= synonym
.getSynonymRelations();
321 for (SynonymRelationship sr
: synRelationships
) {
322 Taxon accTaxon
= sr
.getAcceptedTaxon();
323 accTbSet
.add(accTaxon
);
329 // update name search object
330 ns
.addToResponseList(nvn
.getTitleCache(), nvn
.getNameCache(), nvn
.getUuid()
331 .toString(), tbSet
, accTbSet
);
336 ErrorResponse er
= new ErrorResponse();
337 er
.setErrorMessage("No Taxon Name for given query : " + query
);
341 mv
.addObject(nsList
);
347 * Returns a documentation page for the Name Information API.
349 * URI: <b>/{datasource-name}/name_catalogue/name</b>
351 * @param request Http servlet request.
352 * @param response Http servlet response.
353 * @return Html page describing the Name Information API
354 * @throws IOException
356 @RequestMapping(value
= { "name" }, method
= RequestMethod
.GET
, params
= {})
357 public ModelAndView
doGetNameInformationDocumentation(
358 HttpServletRequest request
, HttpServletResponse response
)
360 ModelAndView mv
= new ModelAndView();
361 // Read apt documentation file.
362 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-name-info.apt");
363 // using input stream as this works for both files in the classes directory
364 // as well as files inside jars
365 InputStream aptInputStream
= resource
.getInputStream();
367 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
368 // Convert Apt to Html
369 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
370 mv
.addAllObjects(modelMap
);
372 HtmlView hv
= new HtmlView();
378 * Returns information related to the scientific name matching the given
379 * <code>{nameUuid}</code>. The information includes the name string,
380 * relationships, rank, list of related lsids / taxon uuids, etc.
382 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-name-info.html">here</a>
384 * URI: <b>/{datasource-name}/name_catalogue/name</b>
386 * @param nameUuids uuid(s) of the scientific name to search for.
387 * @param request Http servlet request.
388 * @param response Http servlet response.
389 * @return a List of {@link NameInformation} objects each corresponding to a
390 * single name uuid. These are built from {@link TaxonNameBase} entities
391 * which are in turn initialized using the {@link #NAME_INFORMATION_INIT_STRATEGY}
392 * @throws IOException
394 @RequestMapping(value
= { "name" }, method
= RequestMethod
.GET
, params
= {"nameUuid"})
395 public ModelAndView
doGetNameInformation(@RequestParam(value
= "nameUuid", required
= true) String
[] nameUuids
,
396 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
397 ModelAndView mv
= new ModelAndView();
398 List
<RemoteResponse
> niList
= new ArrayList
<RemoteResponse
>();
399 // loop through each name uuid
400 for (String nameUuid
: nameUuids
) {
401 logger
.info("doGetNameInformation()" + request
.getServletPath() + " for name uuid \""
404 NonViralName nvn
= (NonViralName
) service
.findNameByUuid(UUID
.fromString(nameUuid
),
405 NAME_INFORMATION_INIT_STRATEGY
);
407 // if search is successful then get related information, else return error
409 NameInformation ni
= new NameInformation();
410 ni
.setRequest(nameUuid
);
411 Reference ref
= (Reference
) nvn
.getNomenclaturalReference();
412 String citation
= "";
413 String citation_details
= "";
415 citation
= ref
.getTitleCache();
417 // update name information object
418 ni
.setResponse(nvn
.getTitleCache(), nvn
.getNameCache(), nvn
.getRank().getTitleCache(),
419 nvn
.getStatus(), citation
, nvn
.getRelationsFromThisName(),
420 nvn
.getRelationsToThisName(), nvn
.getTaxonBases());
423 ErrorResponse re
= new ErrorResponse();
425 if(isValid(nameUuid
)) {
426 re
.setErrorMessage("No Name for given UUID : " + nameUuid
);
428 re
.setErrorMessage(nameUuid
+ " not a valid UUID");
433 mv
.addObject(niList
);
438 * Returns a documentation page for the Taxon Information API.
440 * URI: <b>/{datasource-name}/name_catalogue/taxon</b>
442 * @param request Http servlet request.
443 * @param response Http servlet response.
444 * @return Html page describing the Taxon Information API
445 * @throws IOException
447 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
, params
= {})
448 public ModelAndView
doGetTaxonInformation(
449 HttpServletRequest request
, HttpServletResponse response
)
451 ModelAndView mv
= new ModelAndView();
452 // Read apt documentation file.
453 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-taxon-info.apt");
454 // using input stream as this works for both files in the classes directory
455 // as well as files inside jars
456 InputStream aptInputStream
= resource
.getInputStream();
458 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
459 // Convert Apt to Html
460 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
461 mv
.addAllObjects(modelMap
);
463 HtmlView hv
= new HtmlView();
469 * Returns information related to the taxon matching the given
470 * <code>{taxonUuid}</code>. The information includes the name cache, title cache
471 * relationship type, taxonomic status, information , etc.
473 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
475 * URI: <b>/{datasource-name}/name_catalogue</b>
478 * The taxon uuid to query for. The classification returned corresponds
479 * to the first in the alphabetically sorted list of classifications
480 * currently available in the database.
482 * @param request Http servlet request.
483 * @param response Http servlet response.
484 * @return a List of {@link TaxonInformation} objects each corresponding to a
485 * single query. These are built from {@TaxonBase} entities which are
486 * in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
487 * @throws IOException
489 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
,params
= {"taxonUuid"})
490 public ModelAndView
doGetTaxonInformation(
491 @RequestParam(value
= "taxonUuid", required
= true) String
[] taxonUuids
,
492 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
493 return doGetTaxonInformation(taxonUuids
,CLASSIFICATION_DEFAULT
, request
, response
);
497 * Returns information related to the taxon matching the given
498 * <code>{taxonUuid}</code>. The information includes the name cache, title cache
499 * relationship type, taxonomic status, information , etc.
501 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
503 * URI: <b>/{datasource-name}/name_catalogue/taxon</b>
506 * The taxon uuid to query for.
507 * @param classification
508 * [Optional] String representing the taxonomic classification to use for
509 * building the classification tree. Defaults to the first in the alphabetically
510 * sorted list of classifications currently available in the database.
512 * @param request Http servlet request.
513 * @param response Http servlet response.
514 * @return a List of {@link TaxonInformation} objects each corresponding to a
515 * single query. These are built from {@TaxonBase} entities which are
516 * in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
517 * @throws IOException
519 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
, params
= {"taxonUuid", "classification"})
520 public ModelAndView
doGetTaxonInformation(
521 @RequestParam(value
= "taxonUuid", required
= true) String
[] taxonUuids
,
522 @RequestParam(value
= "classification", required
= false, defaultValue
= CLASSIFICATION_DEFAULT
) String classificationType
,
523 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
524 ModelAndView mv
= new ModelAndView();
525 List
<RemoteResponse
> tiList
= new ArrayList
<RemoteResponse
>();
526 // loop through each name uuid
527 for (String taxonUuid
: taxonUuids
) {
528 logger
.info("doGetTaxonInformation()" + request
.getServletPath() + " for taxon uuid \""
531 TaxonBase tb
= taxonService
.findTaxonByUuid(UUID
.fromString(taxonUuid
),
532 TAXON_INFORMATION_INIT_STRATEGY
);
533 // if search is successful then get related information, else return error
535 TaxonInformation ti
= new TaxonInformation();
536 ti
.setRequest(taxonUuid
);
537 // check if result (taxon base) is a taxon or synonym
538 if (tb
.isInstanceOf(Taxon
.class)) {
539 Taxon taxon
= (Taxon
) tb
;
540 // build classification map
541 Map classificationMap
= getClassification(taxon
, classificationType
);
543 logger
.info("taxon uuid " + taxon
.getUuid().toString() + " original hash code : " + System
.identityHashCode(taxon
) + ", name class " + taxon
.getName().getClass().getName());
544 // update taxon information object with taxon related data
545 NonViralName nvn
= (NonViralName
) taxon
.getName();
546 ti
.setResponseTaxon(tb
.getTitleCache(),
548 nvn
.getRank().getTitleCache(),
549 ACCEPTED_NAME_STATUS
,
553 Set
<SynonymRelationship
> synRelationships
= taxon
.getSynonymRelations();
554 // add synonyms (if exists) to taxon information object
555 for (SynonymRelationship sr
: synRelationships
) {
556 Synonym syn
= sr
.getSynonym();
557 String uuid
= syn
.getUuid().toString();
558 String title
= syn
.getTitleCache();
559 TaxonNameBase synnvn
= (TaxonNameBase
) syn
.getName();
560 String name
= synnvn
.getTitleCache();
561 String rank
= synnvn
.getRank().getTitleCache();
562 String status
= SYNONYM_STATUS
;
563 String relLabel
= sr
.getType()
564 .getInverseRepresentation(Language
.DEFAULT())
566 ti
.addToResponseRelatedTaxa(uuid
, title
, name
, rank
, status
, "", relLabel
);
569 // build relationship information as,
570 // - relationships from the requested taxon
571 Set
<TaxonRelationship
> trFromSet
= taxon
.getRelationsFromThisTaxon();
572 for (TaxonRelationship tr
: trFromSet
) {
573 String titleTo
= tr
.getToTaxon().getTitleCache();
574 TaxonNameBase tonvn
= (TaxonNameBase
) tr
.getToTaxon().getName();
575 String name
= tonvn
.getTitleCache();
576 String rank
= tonvn
.getRank().getTitleCache();
577 String uuid
= tr
.getToTaxon().getUuid().toString();
578 String status
= ACCEPTED_NAME_STATUS
;
579 String relLabel
= tr
.getType().getRepresentation(Language
.DEFAULT())
581 ti
.addToResponseRelatedTaxa(uuid
, titleTo
, name
, rank
, status
, "", relLabel
);
582 //logger.info("titleTo : " + titleTo + " , name : " + name);
585 // - relationships to the requested taxon
586 Set
<TaxonRelationship
> trToSet
= taxon
.getRelationsToThisTaxon();
587 for (TaxonRelationship tr
: trToSet
) {
588 String titleFrom
= tr
.getFromTaxon().getTitleCache();
589 TaxonNameBase fromnvn
= (TaxonNameBase
) tr
.getFromTaxon().getName();
590 String name
= fromnvn
.getTitleCache();
591 String rank
= fromnvn
.getRank().getTitleCache();
592 String uuid
= tr
.getFromTaxon().getUuid().toString();
593 String status
= ACCEPTED_NAME_STATUS
;
594 String relLabel
= tr
.getType()
595 .getInverseRepresentation(Language
.DEFAULT())
597 ti
.addToResponseRelatedTaxa(uuid
, titleFrom
, name
, rank
, status
, "", relLabel
);
598 //logger.info("titleFrom : " + titleFrom + " , name : " + name);
600 } else if (tb
instanceof Synonym
) {
601 Synonym synonym
= (Synonym
) tb
;
602 TaxonNameBase nvn
= (TaxonNameBase
) synonym
.getName();
603 // update taxon information object with synonym related data
604 ti
.setResponseTaxon(synonym
.getTitleCache(),
606 nvn
.getRank().getTitleCache(),
608 buildFlagMap(synonym
),
609 new TreeMap
<String
,Map
>());
610 // add accepted taxa (if exists) to taxon information object
612 Set
<SynonymRelationship
> synRelationships
= synonym
.getSynonymRelations();
613 for (SynonymRelationship sr
: synRelationships
) {
614 Taxon accTaxon
= sr
.getAcceptedTaxon();
615 String uuid
= accTaxon
.getUuid().toString();
616 logger
.info("acc taxon uuid " + accTaxon
.getUuid().toString() + " original hash code : " + System
.identityHashCode(accTaxon
) + ", name class " + accTaxon
.getName().getClass().getName());
617 String title
= accTaxon
.getTitleCache();
618 logger
.info("taxon title cache : " + accTaxon
.getTitleCache());
620 TaxonNameBase accnvn
= (TaxonNameBase
)accTaxon
.getName();
621 String name
= accnvn
.getTitleCache();
622 String rank
= accnvn
.getRank().getTitleCache();
623 String status
= ACCEPTED_NAME_STATUS
;
624 String relLabel
= sr
.getType().getRepresentation(Language
.DEFAULT())
626 ti
.addToResponseRelatedTaxa(uuid
, title
, name
, rank
, status
, "", relLabel
);
632 ErrorResponse re
= new ErrorResponse();
633 if(isValid(taxonUuid
)) {
634 re
.setErrorMessage("No Taxon for given UUID : " + taxonUuid
);
636 re
.setErrorMessage(taxonUuid
+ " not a valid UUID");
641 mv
.addObject(tiList
);
646 * Returns a list of all available classifications (with associated referenc information) and the default classification.
648 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-classification-info.html">here</a>
650 * URI: <b>/{datasource-name}/name_catalogue/voc/classification</b>
652 * @param request Http servlet request.
653 * @param response Http servlet response.
654 * @return a List of {@link Classification} objects represebted as strings.
655 * These are initialized using the {@link #VOC_CLASSIFICATION_INIT_STRATEGY}
656 * @throws IOException
658 @RequestMapping(value
= { "voc/classification" }, method
= RequestMethod
.GET
, params
= {})
659 public ModelAndView
doGetClassificationMap(HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
660 List
<Map
> cmapList
= new ArrayList
<Map
>();
661 Map
<String
, String
> classifications
= new HashMap
<String
, String
>();
662 ModelAndView mv
= new ModelAndView();
663 List
<Classification
> clist
= getClassificationList(100);
664 boolean isFirst
= true;
665 Iterator itr
= clist
.iterator();
666 // loop through all classifications and populate map with
667 // (classificationKey, reference) elements
668 while(itr
.hasNext()) {
669 Classification c
= (Classification
) itr
.next();
670 String refTitleCache
= "";
671 String classificationKey
= removeInternalWhitespace(c
.getTitleCache());
672 if(c
.getReference() != null) {
673 refTitleCache
= c
.getReference().getTitleCache();
675 // default is the first element of the list
676 // always created with the same sorting (DESCENDING)
678 Map
<String
, String
> defaultMap
= new HashMap
<String
, String
>();
679 defaultMap
.put("default", classificationKey
);
680 cmapList
.add(defaultMap
);
683 classifications
.put(classificationKey
, refTitleCache
);
686 Map
<String
, Map
> cmap
= new HashMap
<String
, Map
>();
687 cmap
.put("classification",classifications
);
689 mv
.addObject(cmapList
);
694 * Returns the match mode by parsing the input string of wildcards.
699 * @return {@link MatchMode} depending on the the position of the wildcard (*)
701 private MatchMode
getMatchModeFromQuery(String query
) {
702 if (query
.startsWith("*") && query
.endsWith("*")) {
703 return MatchMode
.ANYWHERE
;
704 } else if (query
.startsWith("*")) {
705 return MatchMode
.END
;
706 } else if (query
.endsWith("*")) {
707 return MatchMode
.BEGINNING
;
709 return MatchMode
.EXACT
;
714 * Removes wildcards from the input string.
719 * @return input string with wildcards removed
721 private String
getQueryWithoutWildCards(String query
) {
723 String newQuery
= query
;
725 if (query
.startsWith("*")) {
726 newQuery
= newQuery
.substring(1, newQuery
.length());
729 if (query
.endsWith("*")) {
730 newQuery
= newQuery
.substring(0, newQuery
.length() - 1);
733 return newQuery
.trim();
737 * Build map with taxon flag key-value pairs.
739 private Map
<String
, String
> buildFlagMap(TaxonBase tb
) {
740 Map
<String
, String
> flags
= new Hashtable
<String
, String
>();
741 flags
.put(DOUBTFUL_FLAG
, Boolean
.toString(tb
.isDoubtful()));
746 * Build classification map.
748 private Map
<String
, Map
> getClassification(Taxon taxon
, String classificationType
) {
749 // Using TreeMap is important, because we need the sorting of the classification keys
750 // in the map to be stable.
751 TreeMap
<String
, Map
> sourceClassificationMap
= buildClassificationMap(taxon
, classificationType
);
753 // if classification key is 'default' then return the default element of the map
754 if(classificationType
.equals(CLASSIFICATION_DEFAULT
) && !sourceClassificationMap
.isEmpty()) {
755 List
<Classification
> clist
= getClassificationList(1);
756 String defaultKey
= removeInternalWhitespace(clist
.get(0).getTitleCache());
757 return sourceClassificationMap
.get(defaultKey
);
758 // if classification key is provided then return the classification corresponding to the key
759 } else if(sourceClassificationMap
.containsKey(classificationType
)) {
760 return sourceClassificationMap
.get(classificationType
);
761 // if classification key is 'all' then return the entire map
762 } else if(classificationType
.equals(CLASSIFICATION_ALL
)) {
763 return sourceClassificationMap
;
765 return new TreeMap
<String
,Map
>();
770 * Build classification map.
772 private TreeMap
<String
, Map
> buildClassificationMap(Taxon taxon
, String classificationType
) {
773 // Using TreeMap is important, because we need the sorting of the classification keys
774 // in the map to be stable.
775 TreeMap
<String
, Map
> sourceClassificationMap
= new TreeMap
<String
, Map
>();
776 Set
<TaxonNode
> taxonNodes
= taxon
.getTaxonNodes();
777 //loop through taxon nodes and build classification map for each classification key
778 for (TaxonNode tn
: taxonNodes
) {
779 Map
<String
, String
> classificationMap
= new LinkedHashMap
<String
, String
>();
780 List
<TaxonNode
> tnList
= classificationService
.loadTreeBranchToTaxon(taxon
,
781 tn
.getClassification(), null, TAXON_NODE_INIT_STRATEGY
);
782 for (TaxonNode classificationtn
: tnList
) {
783 classificationMap
.put(classificationtn
.getTaxon().getName().getRank().getTitleCache(),
784 classificationtn
.getTaxon().getName().getTitleCache());
786 String cname
= removeInternalWhitespace(tn
.getClassification().getTitleCache());
787 logger
.info("Building classification map " + cname
);
788 sourceClassificationMap
.put(cname
, classificationMap
);
790 return sourceClassificationMap
;
793 private String
removeInternalWhitespace(String withWSpace
) {
794 String
[] words
= withWSpace
.split("\\s+");
795 // "\\s+" in regular expression language meaning one or
797 StringBuilder builder
= new StringBuilder();
798 for (String word
: words
) {
799 builder
.append(word
);
801 return builder
.toString();
804 private List
<Classification
> getClassificationList(int limit
) {
805 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
806 orderHints
.add(new OrderHint("titleCache", SortOrder
.DESCENDING
));
807 List
<Classification
> clist
= classificationService
.listClassifications(limit
, 0, orderHints
, VOC_CLASSIFICATION_INIT_STRATEGY
);
811 private boolean isValid(String uuid
){
812 if( uuid
== null) return false;
814 // we have to convert to object and back to string because the built in fromString does not have
815 // good validation logic.
817 UUID fromStringUUID
= UUID
.fromString(uuid
);
818 String toStringUUID
= fromStringUUID
.toString();
820 System
.out
.println("input uuid : " + uuid
+ " , parsed uuid : " + toStringUUID
);
821 return toStringUUID
.equals(uuid
);
822 } catch(IllegalArgumentException e
) {
827 public void setResourceLoader(ResourceLoader resourceLoader
) {
828 this.resourceLoader
= resourceLoader
;