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
.Language
;
47 import eu
.etaxonomy
.cdm
.model
.name
.NonViralName
;
48 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
49 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
50 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
51 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
52 import eu
.etaxonomy
.cdm
.model
.taxon
.SynonymRelationship
;
53 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
54 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
55 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
56 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
57 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
58 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
59 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
62 * The controller class for the namespace 'name_catalogue'. This web service namespace
63 * is an add-on to the already existing CDM REST API and provides information relating
64 * to scientific names as well as taxa present in the underlying datasource.
68 * @created 15-Apr-2012
72 @RequestMapping(value
= { "/name_catalogue" })
73 public class NameCatalogueController
extends BaseController
<TaxonNameBase
, INameService
> implements ResourceLoaderAware
{
75 private ResourceLoader resourceLoader
;
77 /** Taxonomic status 'accepted' string */
78 public static final String ACCEPTED_NAME_STATUS
= "accepted";
80 /** Taxonpmic status 'synonym' string */
81 public static final String SYNONYM_STATUS
= "synonym";
83 /** Flag 'doubtful' strings */
84 public static final String DOUBTFUL_FLAG
= "doubtful";
86 /** Base scientific name search type */
87 public static final String NAME_SEARCH
= "name";
89 /** Complete scientific name search type */
90 public static final String TITLE_SEARCH
= "title";
92 /** Default name search type */
93 public static final String DEFAULT_SEARCH_TYPE
= NAME_SEARCH
;
95 /** Classifcation 'default' key */
96 public static final String CLASSIFICATION_DEFAULT
= "default";
98 /** Classifcation 'all' key */
99 public static final String CLASSIFICATION_ALL
= "all";
102 private ITaxonService taxonService
;
105 private IClassificationService classificationService
;
107 /** Hibernate name search initialisation strategy */
108 private static final List
<String
> NAME_SEARCH_INIT_STRATEGY
= Arrays
.asList(new String
[] {
109 "combinationAuthorTeam.$",
110 "exCombinationAuthorTeam.$",
111 "basionymAuthorTeam.$",
112 "exBasionymAuthorTeam.$",
115 "taxonBases.synonymRelations.type.$"});
117 /** Hibernate name information initialisation strategy */
118 private static final List
<String
> NAME_INFORMATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
121 "nomenclaturalReference.$",
122 "combinationAuthorTeam.$",
123 "exCombinationAuthorTeam.$",
124 "basionymAuthorTeam.$",
125 "exBasionymAuthorTeam.$",
126 "relationsToThisName.$",
127 "relationsFromThisName.$" });
129 /** Hibernate taxon information initialisation strategy */
130 private static final List
<String
> TAXON_INFORMATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
131 "name.rank.titleCache",
132 "synonymRelations.synonym.name.rank.titleCache",
133 "synonymRelations.type.$",
134 "synonymRelations.relatedTo.name.rank.titleCache",
135 "relationsFromThisTaxon.type.$",
136 "relationsFromThisTaxon.relatedTo.name.rank.titleCache",
137 "relationsToThisTaxon.type.$",
138 "relationsToThisTaxon.relatedFrom.name.rank.titleCache",
140 "taxonNodes.classification" });
142 /** Hibernate taxon node initialisation strategy */
143 private static final List
<String
> TAXON_NODE_INIT_STRATEGY
= Arrays
.asList(new String
[] {
147 "classification.reference.$",
148 "classification.reference.authorTeam.$" });
150 /** Hibernate classification vocabulary initialisation strategy */
151 private static final List
<String
> VOC_CLASSIFICATION_INIT_STRATEGY
= Arrays
.asList(new String
[] {
153 "classification.reference.$",
154 "classification.reference.authorTeam.$" });
155 public NameCatalogueController() {
157 setInitializationStrategy(Arrays
.asList(new String
[] { "$" }));
164 * eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu
165 * .etaxonomy.cdm.api.service.IService)
169 public void setService(INameService service
) {
170 this.service
= service
;
174 * Returns a documentation page for the Name Search API.
176 * URI: <b>/{datasource-name}/name_catalogue</b>
180 * @return Html page describing the Name Search API
181 * @throws IOException
183 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {})
184 public ModelAndView
doGetNameSearchDocumentation(
185 HttpServletRequest request
, HttpServletResponse response
)
187 ModelAndView mv
= new ModelAndView();
188 // Read apt documentation file.
189 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-default.apt");
190 // using input stream as this works for both files in the classes directory
191 // as well as files inside jars
192 InputStream aptInputStream
= resource
.getInputStream();
194 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
195 // Convert Apt to Html
196 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
197 mv
.addAllObjects(modelMap
);
199 HtmlView hv
= new HtmlView();
205 * Returns a list of scientific names matching the <code>{query}</code>
206 * string pattern. Each of these scientific names is accompanied by a list of
207 * name uuids, a list of taxon uuids, a list of accepted taxon uuids, etc.
209 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
211 * URI: <b>/{datasource-name}/name_catalogue</b>
214 * The base scientific name pattern(s) to query for. The query can
215 * contain wildcard characters ('*'). The query can be
216 * performed with no wildcard or with the wildcard at the
217 * begin and / or end depending on the search pattern.
218 * @param request Http servlet request.
219 * @param response Http servlet response.
220 * @return a list of {@link NameSearch} objects each corresponding to a
221 * single query. These are built from {@link TaxonNameBase}
222 * entities which are in turn initialized using
223 * the {@link #NAME_SEARCH_INIT_STRATEGY}
224 * @throws IOException
226 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {"query"})
227 public ModelAndView
doGetNameSearch(@RequestParam(value
= "query", required
= true) String
[] queries
,
228 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
229 return doGetNameSearch(queries
, DEFAULT_SEARCH_TYPE
, request
, response
);
233 * Returns a list of scientific names matching the <code>{query}</code>
234 * string pattern. Each of these scientific names is accompanied by a list of
235 * name uuids, a list of taxon uuids and a list of accepted taxon uuids.
237 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
239 * URI: <b>/{datasource-name}/name_catalogue</b>
242 * The scientific name pattern(s) to query for. The query can
243 * contain wildcard characters ('*'). The query can be
244 * performed with no wildcard or with the wildcard at the
245 * begin and / or end depending on the search pattern.
247 * The type of name to query. This be either
248 * "name" : scientific name corresponding to 'name cache' in CDM or
249 * "title" : complete name corresponding to 'title cache' in CDM
250 * @param request Http servlet request.
251 * @param response Http servlet response.
252 * @return a List of {@link NameSearch} objects each corresponding to a
253 * single query. These are built from {@link TaxonNameBase} entities
254 * which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
255 * @throws IOException
257 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {"query", "type"})
258 public ModelAndView
doGetNameSearch(@RequestParam(value
= "query", required
= true) String
[] queries
,
259 @RequestParam(value
= "type", required
= false, defaultValue
= DEFAULT_SEARCH_TYPE
) String searchType
,
260 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
261 ModelAndView mv
= new ModelAndView();
262 List
<RemoteResponse
> nsList
= new ArrayList
<RemoteResponse
>();
264 // if search type is not known then return error
265 if (!searchType
.equals(NAME_SEARCH
) && !searchType
.equals(TITLE_SEARCH
)) {
266 ErrorResponse er
= new ErrorResponse();
267 er
.setErrorMessage("searchType parameter can only be set as" + NAME_SEARCH
+ " or "
273 // search through each query
274 for (String query
: queries
) {
276 String queryWOWildcards
= getQueryWithoutWildCards(query
);
277 MatchMode mm
= getMatchModeFromQuery(query
);
278 logger
.info("doGetNameSearch()" + request
.getServletPath() + " for query \"" + query
279 + "\" without wild cards : " + queryWOWildcards
+ " and match mode : " + mm
);
280 List
<NonViralName
> nameList
= new ArrayList
<NonViralName
>();
282 // if "name" search then find by name cache
283 if (searchType
.equals(NAME_SEARCH
)) {
284 nameList
= (List
<NonViralName
>) service
.findNamesByNameCache(queryWOWildcards
, mm
,
285 NAME_SEARCH_INIT_STRATEGY
);
288 //if "title" search then find by title cache
289 if (searchType
.equals(TITLE_SEARCH
)) {
290 nameList
= (List
<NonViralName
>) service
.findNamesByTitleCache(queryWOWildcards
, mm
,
291 NAME_SEARCH_INIT_STRATEGY
);
294 // if search is successful then get related information , else return error
295 if (nameList
== null || !nameList
.isEmpty()) {
296 NameSearch ns
= new NameSearch();
297 ns
.setRequest(query
);
299 for (NonViralName nvn
: nameList
) {
300 // we need to retrieve both taxon uuid of name queried and
301 // the corresponding accepted taxa.
302 // reason to return accepted taxa also, is to be able to get from
303 // scientific name to taxon concept in two web service calls.
304 Set
<TaxonBase
> tbSet
= nvn
.getTaxonBases();
305 Set
<TaxonBase
> accTbSet
= new HashSet
<TaxonBase
>();
306 for (TaxonBase tb
: tbSet
) {
307 // if synonym then get accepted taxa.
308 if (tb
instanceof Synonym
) {
309 Synonym synonym
= (Synonym
) tb
;
310 Set
<SynonymRelationship
> synRelationships
= synonym
.getSynonymRelations();
311 for (SynonymRelationship sr
: synRelationships
) {
312 Taxon accTaxon
= sr
.getAcceptedTaxon();
313 accTbSet
.add(accTaxon
);
319 // update name search object
320 ns
.addToResponseList(nvn
.getTitleCache(), nvn
.getNameCache(), nvn
.getUuid()
321 .toString(), tbSet
, accTbSet
);
326 ErrorResponse er
= new ErrorResponse();
327 er
.setErrorMessage("No Taxon Name for given query : " + query
);
331 mv
.addObject(nsList
);
337 * Returns a documentation page for the Name Information API.
339 * URI: <b>/{datasource-name}/name_catalogue/name</b>
341 * @param request Http servlet request.
342 * @param response Http servlet response.
343 * @return Html page describing the Name Information API
344 * @throws IOException
346 @RequestMapping(value
= { "name" }, method
= RequestMethod
.GET
, params
= {})
347 public ModelAndView
doGetNameInformationDocumentation(
348 HttpServletRequest request
, HttpServletResponse response
)
350 ModelAndView mv
= new ModelAndView();
351 // Read apt documentation file.
352 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-name-info.apt");
353 // using input stream as this works for both files in the classes directory
354 // as well as files inside jars
355 InputStream aptInputStream
= resource
.getInputStream();
357 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
358 // Convert Apt to Html
359 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
360 mv
.addAllObjects(modelMap
);
362 HtmlView hv
= new HtmlView();
368 * Returns information related to the scientific name matching the given
369 * <code>{nameUuid}</code>. The information includes the name string,
370 * relationships, rank, list of related lsids / taxon uuids, etc.
372 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-name-info.html">here</a>
374 * URI: <b>/{datasource-name}/name_catalogue/name</b>
376 * @param nameUuids uuid(s) of the scientific name to search for.
377 * @param request Http servlet request.
378 * @param response Http servlet response.
379 * @return a List of {@link NameInformation} objects each corresponding to a
380 * single name uuid. These are built from {@link TaxonNameBase} entities
381 * which are in turn initialized using the {@link #NAME_INFORMATION_INIT_STRATEGY}
382 * @throws IOException
384 @RequestMapping(value
= { "name" }, method
= RequestMethod
.GET
, params
= {"nameUuid"})
385 public ModelAndView
doGetNameInformation(@RequestParam(value
= "nameUuid", required
= true) String
[] nameUuids
,
386 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
387 ModelAndView mv
= new ModelAndView();
388 List
<RemoteResponse
> niList
= new ArrayList
<RemoteResponse
>();
389 // loop through each name uuid
390 for (String nameUuid
: nameUuids
) {
391 logger
.info("doGetNameInformation()" + request
.getServletPath() + " for name uuid \""
394 NonViralName nvn
= (NonViralName
) service
.findNameByUuid(UUID
.fromString(nameUuid
),
395 NAME_INFORMATION_INIT_STRATEGY
);
397 // if search is successful then get related information, else return error
399 NameInformation ni
= new NameInformation();
400 ni
.setRequest(nameUuid
);
401 Reference ref
= (Reference
) nvn
.getNomenclaturalReference();
402 String citation
= "";
403 String citation_details
= "";
405 citation
= ref
.getTitleCache();
407 // update name information object
408 ni
.setResponse(nvn
.getTitleCache(), nvn
.getNameCache(), nvn
.getRank().getTitleCache(),
409 nvn
.getStatus(), citation
, nvn
.getRelationsFromThisName(),
410 nvn
.getRelationsToThisName(), nvn
.getTaxonBases());
413 ErrorResponse re
= new ErrorResponse();
415 if(isValid(nameUuid
)) {
416 re
.setErrorMessage("No Name for given UUID : " + nameUuid
);
418 re
.setErrorMessage(nameUuid
+ " not a valid UUID");
423 mv
.addObject(niList
);
428 * Returns a documentation page for the Taxon Information API.
430 * URI: <b>/{datasource-name}/name_catalogue/taxon</b>
432 * @param request Http servlet request.
433 * @param response Http servlet response.
434 * @return Html page describing the Taxon Information API
435 * @throws IOException
437 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
, params
= {})
438 public ModelAndView
doGetTaxonInformation(
439 HttpServletRequest request
, HttpServletResponse response
)
441 ModelAndView mv
= new ModelAndView();
442 // Read apt documentation file.
443 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-taxon-info.apt");
444 // using input stream as this works for both files in the classes directory
445 // as well as files inside jars
446 InputStream aptInputStream
= resource
.getInputStream();
448 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
449 // Convert Apt to Html
450 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
451 mv
.addAllObjects(modelMap
);
453 HtmlView hv
= new HtmlView();
459 * Returns information related to the taxon matching the given
460 * <code>{taxonUuid}</code>. The information includes the name cache, title cache
461 * relationship type, taxonomic status, information , etc.
463 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
465 * URI: <b>/{datasource-name}/name_catalogue</b>
468 * The taxon uuid to query for. The classification returned corresponds
469 * to the first in the alphabetically sorted list of classifications
470 * currently available in the database.
472 * @param request Http servlet request.
473 * @param response Http servlet response.
474 * @return a List of {@link TaxonInformation} objects each corresponding to a
475 * single query. These are built from {@TaxonBase} entities which are
476 * in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
477 * @throws IOException
479 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
,params
= {"taxonUuid"})
480 public ModelAndView
doGetTaxonInformation(
481 @RequestParam(value
= "taxonUuid", required
= true) String
[] taxonUuids
,
482 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
483 return doGetTaxonInformation(taxonUuids
,CLASSIFICATION_DEFAULT
, request
, response
);
487 * Returns information related to the taxon matching the given
488 * <code>{taxonUuid}</code>. The information includes the name cache, title cache
489 * relationship type, taxonomic status, information , etc.
491 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
493 * URI: <b>/{datasource-name}/name_catalogue/taxon</b>
496 * The taxon uuid to query for.
497 * @param classification
498 * [Optional] String representing the taxonomic classification to use for
499 * building the classification tree. Defaults to the first in the alphabetically
500 * sorted list of classifications currently available in the database.
502 * @param request Http servlet request.
503 * @param response Http servlet response.
504 * @return a List of {@link TaxonInformation} objects each corresponding to a
505 * single query. These are built from {@TaxonBase} entities which are
506 * in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
507 * @throws IOException
509 @RequestMapping(value
= { "taxon" }, method
= RequestMethod
.GET
, params
= {"taxonUuid", "classification"})
510 public ModelAndView
doGetTaxonInformation(
511 @RequestParam(value
= "taxonUuid", required
= true) String
[] taxonUuids
,
512 @RequestParam(value
= "classification", required
= false, defaultValue
= CLASSIFICATION_DEFAULT
) String classificationType
,
513 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
514 ModelAndView mv
= new ModelAndView();
515 List
<RemoteResponse
> tiList
= new ArrayList
<RemoteResponse
>();
516 // loop through each name uuid
517 for (String taxonUuid
: taxonUuids
) {
518 logger
.info("doGetTaxonInformation()" + request
.getServletPath() + " for taxon uuid \""
521 TaxonBase tb
= taxonService
.findTaxonByUuid(UUID
.fromString(taxonUuid
),
522 TAXON_INFORMATION_INIT_STRATEGY
);
523 // if search is successful then get related information, else return error
525 TaxonInformation ti
= new TaxonInformation();
526 ti
.setRequest(taxonUuid
);
527 // check if result (taxon base) is a taxon or synonym
528 if (tb
.isInstanceOf(Taxon
.class)) {
529 Taxon taxon
= (Taxon
) tb
;
530 // build classification map
531 Map classificationMap
= getClassification(taxon
, classificationType
);
534 // update taxon information object with taxon related data
535 NonViralName nvn
= (NonViralName
) tb
.getName();
536 ti
.setResponseTaxon(tb
.getTitleCache(),
538 nvn
.getRank().getTitleCache(),
539 ACCEPTED_NAME_STATUS
,
543 Set
<SynonymRelationship
> synRelationships
= taxon
.getSynonymRelations();
544 // add synonyms (if exists) to taxon information object
545 for (SynonymRelationship sr
: synRelationships
) {
546 Synonym syn
= sr
.getSynonym();
547 String uuid
= syn
.getUuid().toString();
548 String title
= syn
.getTitleCache();
549 NonViralName synnvn
= (NonViralName
) syn
.getName();
550 String name
= synnvn
.getTitleCache();
551 String rank
= synnvn
.getRank().getTitleCache();
552 String status
= SYNONYM_STATUS
;
553 String relLabel
= sr
.getType()
554 .getInverseRepresentation(Language
.DEFAULT())
556 ti
.addToResponseRelatedTaxa(uuid
, title
, name
, rank
, status
, "", relLabel
);
559 // build relationship information as,
560 // - relationships from the requested taxon
561 Set
<TaxonRelationship
> trFromSet
= taxon
.getRelationsFromThisTaxon();
562 for (TaxonRelationship tr
: trFromSet
) {
563 String titleTo
= tr
.getRelatedTo().getTitleCache();
564 NonViralName tonvn
= (NonViralName
) tr
.getRelatedTo().getName();
565 String name
= tonvn
.getTitleCache();
566 String rank
= tonvn
.getRank().getTitleCache();
567 String uuid
= tr
.getRelatedTo().getUuid().toString();
568 String status
= ACCEPTED_NAME_STATUS
;
569 String relLabel
= tr
.getType().getRepresentation(Language
.DEFAULT())
571 ti
.addToResponseRelatedTaxa(uuid
, titleTo
, name
, rank
, status
, "", relLabel
);
574 // - relationships from the requested taxon
575 Set
<TaxonRelationship
> trToSet
= taxon
.getRelationsToThisTaxon();
576 for (TaxonRelationship tr
: trToSet
) {
577 String titleFrom
= tr
.getRelatedFrom().getTitleCache();
578 NonViralName fromnvn
= (NonViralName
) tr
.getRelatedTo().getName();
579 String name
= fromnvn
.getTitleCache();
580 String rank
= fromnvn
.getRank().getTitleCache();
581 String uuid
= tr
.getRelatedFrom().getUuid().toString();
582 String status
= ACCEPTED_NAME_STATUS
;
583 String relLabel
= tr
.getType()
584 .getInverseRepresentation(Language
.DEFAULT())
586 ti
.addToResponseRelatedTaxa(uuid
, titleFrom
, name
, rank
, status
, "", relLabel
);
588 } else if (tb
instanceof Synonym
) {
589 Synonym synonym
= (Synonym
) tb
;
590 NonViralName nvn
= (NonViralName
) synonym
.getName();
591 // update taxon information object with synonym related data
592 ti
.setResponseTaxon(synonym
.getTitleCache(),
594 nvn
.getRank().getTitleCache(),
596 buildFlagMap(synonym
),
597 new TreeMap
<String
,Map
>());
598 // add accepted taxa (if exists) to taxon information object
600 //FIXME commenting out below line in order to allow fixing the bug #3064 (portal synonymy service produces massive response for Crepis tectorum)
601 /* Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();
602 for (SynonymRelationship sr : synRelationships) {
603 Taxon accTaxon = sr.getRelatedTo();
604 String uuid = accTaxon.getUuid().toString();
605 String title = accTaxon.getTitleCache();
606 NonViralName accnvn = (NonViralName) accTaxon.getName();
607 String name = accnvn.getTitleCache();
608 String rank = accnvn.getRank().getTitleCache();
609 String status = ACCEPTED_NAME_STATUS;
610 String relLabel = sr.getType().getRepresentation(Language.DEFAULT())
612 ti.addToResponseRelatedTaxa(uuid, title, name, rank, status, "", relLabel);
618 ErrorResponse re
= new ErrorResponse();
619 if(isValid(taxonUuid
)) {
620 re
.setErrorMessage("No Taxon for given UUID : " + taxonUuid
);
622 re
.setErrorMessage(taxonUuid
+ " not a valid UUID");
627 mv
.addObject(tiList
);
632 * Returns a list of all available classifications (with associated referenc information) and the default classification.
634 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-classification-info.html">here</a>
636 * URI: <b>/{datasource-name}/name_catalogue/voc/classification</b>
638 * @param request Http servlet request.
639 * @param response Http servlet response.
640 * @return a List of {@link Classification} objects represebted as strings.
641 * These are initialized using the {@link #VOC_CLASSIFICATION_INIT_STRATEGY}
642 * @throws IOException
644 @RequestMapping(value
= { "voc/classification" }, method
= RequestMethod
.GET
, params
= {})
645 public ModelAndView
doGetClassificationMap(HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
646 List
<Map
> cmapList
= new ArrayList
<Map
>();
647 Map
<String
, String
> classifications
= new HashMap
<String
, String
>();
648 ModelAndView mv
= new ModelAndView();
649 List
<Classification
> clist
= getClassificationList(100);
650 boolean isFirst
= true;
651 Iterator itr
= clist
.iterator();
652 // loop through all classifications and populate map with
653 // (classificationKey, reference) elements
654 while(itr
.hasNext()) {
655 Classification c
= (Classification
) itr
.next();
656 String refTitleCache
= "";
657 String classificationKey
= removeInternalWhitespace(c
.getTitleCache());
658 if(c
.getReference() != null) {
659 refTitleCache
= c
.getReference().getTitleCache();
661 // default is the first element of the list
662 // always created with the same sorting (DESCENDING)
664 Map
<String
, String
> defaultMap
= new HashMap
<String
, String
>();
665 defaultMap
.put("default", classificationKey
);
666 cmapList
.add(defaultMap
);
669 classifications
.put(classificationKey
, refTitleCache
);
672 Map
<String
, Map
> cmap
= new HashMap
<String
, Map
>();
673 cmap
.put("classification",classifications
);
675 mv
.addObject(cmapList
);
680 * Returns the match mode by parsing the input string of wildcards.
685 * @return {@link MatchMode} depending on the the position of the wildcard (*)
687 private MatchMode
getMatchModeFromQuery(String query
) {
688 if (query
.startsWith("*") && query
.endsWith("*")) {
689 return MatchMode
.ANYWHERE
;
690 } else if (query
.startsWith("*")) {
691 return MatchMode
.END
;
692 } else if (query
.endsWith("*")) {
693 return MatchMode
.BEGINNING
;
695 return MatchMode
.EXACT
;
700 * Removes wildcards from the input string.
705 * @return input string with wildcards removed
707 private String
getQueryWithoutWildCards(String query
) {
709 String newQuery
= query
;
711 if (query
.startsWith("*")) {
712 newQuery
= newQuery
.substring(1, newQuery
.length());
715 if (query
.endsWith("*")) {
716 newQuery
= newQuery
.substring(0, newQuery
.length() - 1);
719 return newQuery
.trim();
723 * Build map with taxon flag key-value pairs.
725 private Map
<String
, String
> buildFlagMap(TaxonBase tb
) {
726 Map
<String
, String
> flags
= new Hashtable
<String
, String
>();
727 flags
.put(DOUBTFUL_FLAG
, Boolean
.toString(tb
.isDoubtful()));
732 * Build classification map.
734 private Map
<String
, Map
> getClassification(Taxon taxon
, String classificationType
) {
735 // Using TreeMap is important, because we need the sorting of the classification keys
736 // in the map to be stable.
737 TreeMap
<String
, Map
> sourceClassificationMap
= buildClassificationMap(taxon
, classificationType
);
739 // if classification key is 'default' then return the default element of the map
740 if(classificationType
.equals(CLASSIFICATION_DEFAULT
) && !sourceClassificationMap
.isEmpty()) {
741 List
<Classification
> clist
= getClassificationList(1);
742 String defaultKey
= removeInternalWhitespace(clist
.get(0).getTitleCache());
743 return sourceClassificationMap
.get(defaultKey
);
744 // if classification key is provided then return the classification corresponding to the key
745 } else if(sourceClassificationMap
.containsKey(classificationType
)) {
746 return sourceClassificationMap
.get(classificationType
);
747 // if classification key is 'all' then return the entire map
748 } else if(classificationType
.equals(CLASSIFICATION_ALL
)) {
749 return sourceClassificationMap
;
751 return new TreeMap
<String
,Map
>();
756 * Build classification map.
758 private TreeMap
<String
, Map
> buildClassificationMap(Taxon taxon
, String classificationType
) {
759 // Using TreeMap is important, because we need the sorting of the classification keys
760 // in the map to be stable.
761 TreeMap
<String
, Map
> sourceClassificationMap
= new TreeMap
<String
, Map
>();
762 Set
<TaxonNode
> taxonNodes
= taxon
.getTaxonNodes();
763 //loop through taxon nodes and build classification map for each classification key
764 for (TaxonNode tn
: taxonNodes
) {
765 Map
<String
, String
> classificationMap
= new LinkedHashMap
<String
, String
>();
766 List
<TaxonNode
> tnList
= classificationService
.loadTreeBranchToTaxon(taxon
,
767 tn
.getClassification(), null, TAXON_NODE_INIT_STRATEGY
);
768 for (TaxonNode classificationtn
: tnList
) {
769 classificationMap
.put(classificationtn
.getTaxon().getName().getRank().getTitleCache(),
770 classificationtn
.getTaxon().getName().getTitleCache());
772 String cname
= removeInternalWhitespace(tn
.getClassification().getTitleCache());
773 logger
.info("Building classification map " + cname
);
774 sourceClassificationMap
.put(cname
, classificationMap
);
776 return sourceClassificationMap
;
779 private String
removeInternalWhitespace(String withWSpace
) {
780 String
[] words
= withWSpace
.split("\\s+");
781 // "\\s+" in regular expression language meaning one or
783 StringBuilder builder
= new StringBuilder();
784 for (String word
: words
) {
785 builder
.append(word
);
787 return builder
.toString();
790 private List
<Classification
> getClassificationList(int limit
) {
791 List
<OrderHint
> orderHints
= new ArrayList
<OrderHint
>();
792 orderHints
.add(new OrderHint("titleCache", SortOrder
.DESCENDING
));
793 List
<Classification
> clist
= classificationService
.listClassifications(limit
, 0, orderHints
, VOC_CLASSIFICATION_INIT_STRATEGY
);
797 private boolean isValid(String uuid
){
798 if( uuid
== null) return false;
800 // we have to convert to object and back to string because the built in fromString does not have
801 // good validation logic.
803 UUID fromStringUUID
= UUID
.fromString(uuid
);
804 String toStringUUID
= fromStringUUID
.toString();
806 System
.out
.println("input uuid : " + uuid
+ " , parsed uuid : " + toStringUUID
);
807 return toStringUUID
.equals(uuid
);
808 } catch(IllegalArgumentException e
) {
813 public void setResourceLoader(ResourceLoader resourceLoader
) {
814 this.resourceLoader
= resourceLoader
;