reading apt file using input stream instead of file as this works also within jars
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / NameCatalogueController.java
1
2 package eu.etaxonomy.cdm.remote.controller;
3
4 import java.io.File;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.util.ArrayList;
8 import java.util.Arrays;
9 import java.util.HashMap;
10 import java.util.HashSet;
11 import java.util.LinkedHashMap;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.TreeMap;
16 import java.util.UUID;
17
18 import javax.servlet.http.HttpServletRequest;
19 import javax.servlet.http.HttpServletResponse;
20
21 import java.util.Hashtable;
22
23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.context.ResourceLoaderAware;
25 import org.springframework.core.io.Resource;
26 import org.springframework.core.io.ResourceLoader;
27 import org.springframework.stereotype.Controller;
28 import org.springframework.web.bind.annotation.RequestMapping;
29 import org.springframework.web.bind.annotation.RequestMethod;
30 import org.springframework.web.bind.annotation.RequestParam;
31 import org.springframework.web.servlet.ModelAndView;
32
33 import eu.etaxonomy.cdm.api.service.IClassificationService;
34 import eu.etaxonomy.cdm.api.service.INameService;
35 import eu.etaxonomy.cdm.api.service.ITaxonService;
36 import eu.etaxonomy.cdm.common.DocUtils;
37
38 import eu.etaxonomy.cdm.remote.dto.common.ErrorResponse;
39 import eu.etaxonomy.cdm.remote.dto.common.RemoteResponse;
40 import eu.etaxonomy.cdm.remote.dto.namecatalogue.NameInformation;
41 import eu.etaxonomy.cdm.remote.dto.namecatalogue.NameSearch;
42 import eu.etaxonomy.cdm.remote.dto.namecatalogue.TaxonInformation;
43 import eu.etaxonomy.cdm.remote.view.HtmlView;
44 import eu.etaxonomy.cdm.model.common.Language;
45 import eu.etaxonomy.cdm.model.name.NonViralName;
46 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
47 import eu.etaxonomy.cdm.model.reference.Reference;
48 import eu.etaxonomy.cdm.model.taxon.Synonym;
49 import eu.etaxonomy.cdm.model.taxon.SynonymRelationship;
50 import eu.etaxonomy.cdm.model.taxon.Taxon;
51 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
52 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
53 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
54 import eu.etaxonomy.cdm.persistence.query.MatchMode;
55
56 /**
57 * The controller class for the namespace 'name_catalogue'. This web service namespace
58 * is an add-on to the already existing CDM REST API and provides information relating
59 * to scientific names as well as taxa present in the underlying datasource.
60 *
61 * @author c.mathew
62 * @version 1.1.0
63 * @created 15-Apr-2012
64 */
65
66 @Controller
67 @RequestMapping(value = { "/name_catalogue" })
68 public class NameCatalogueController extends BaseController<TaxonNameBase, INameService> implements ResourceLoaderAware {
69
70 private ResourceLoader resourceLoader;
71
72 /** Taxonomic status 'accepted' string */
73 public static final String ACCEPTED_NAME_STATUS = "accepted";
74
75 /** Taxonpmic status 'synonym' string */
76 public static final String SYNONYM_STATUS = "synonym";
77
78 /** Flag 'doubtful' strings */
79 public static final String DOUBTFUL_FLAG = "doubtful";
80
81 /** Base scientific name search type */
82 public static final String NAME_SEARCH = "name";
83
84 /** Complete scientific name search type */
85 public static final String TITLE_SEARCH = "title";
86
87 /** Default name search type */
88 public static final String DEFAULT_SEARCH_TYPE = NAME_SEARCH;
89
90 /** Classifcation 'default' key */
91 public static final String CLASSIFICATION_DEFAULT = "default";
92
93 /** Classifcation 'all' key */
94 public static final String CLASSIFICATION_ALL = "all";
95
96 @Autowired
97 private ITaxonService taxonService;
98
99 @Autowired
100 private IClassificationService classificationService;
101
102 /** Hibernate name search initialisation strategy */
103 private static final List<String> NAME_SEARCH_INIT_STRATEGY = Arrays.asList(new String[] {
104 "combinationAuthorTeam.$",
105 "exCombinationAuthorTeam.$",
106 "basionymAuthorTeam.$",
107 "exBasionymAuthorTeam.$",
108 "nameCache",
109 "taxonBases",
110 "taxonBases.synonymRelations.type.$"});
111
112 /** Hibernate name information initialisation strategy */
113 private static final List<String> NAME_INFORMATION_INIT_STRATEGY = Arrays.asList(new String[] {
114 "taxonBases",
115 "status",
116 "nomenclaturalReference.$",
117 "combinationAuthorTeam.$",
118 "exCombinationAuthorTeam.$",
119 "basionymAuthorTeam.$",
120 "exBasionymAuthorTeam.$",
121 "relationsToThisName.$",
122 "relationsFromThisName.$" });
123
124 /** Hibernate taxon information initialisation strategy */
125 private static final List<String> TAXON_INFORMATION_INIT_STRATEGY = Arrays.asList(new String[] {
126 "synonymRelations.type.$",
127 "relationsFromThisTaxon.type.$",
128 "relationsToThisTaxon.type.$",
129 "taxonNodes",
130 "taxonNodes.classification" });
131
132 /** Hibernate taxon node initialisation strategy */
133 private static final List<String> TAXON_NODE_INIT_STRATEGY = Arrays.asList(new String[] {
134 "taxon.sec",
135 "taxon.name",
136 "classification",
137 "classification.reference.$",
138 "classification.reference.authorTeam.$" });
139
140 public NameCatalogueController() {
141 super();
142 setInitializationStrategy(Arrays.asList(new String[] { "$" }));
143 }
144
145 /*
146 * (non-Javadoc)
147 *
148 * @see
149 * eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu
150 * .etaxonomy.cdm.api.service.IService)
151 */
152 @Autowired
153 @Override
154 public void setService(INameService service) {
155 this.service = service;
156 }
157
158 /**
159 * Returns a documentation page for the Name Search API.
160 * <p>
161 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>
162 *
163 * @param request
164 * @param response
165 * @return Html page describing the Name Search API
166 * @throws IOException
167 */
168 @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {})
169 public ModelAndView doGetNameSearchDocumentation(
170 HttpServletRequest request, HttpServletResponse response)
171 throws IOException {
172 ModelAndView mv = new ModelAndView();
173 // Read apt documentation file.
174 Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-default.apt");
175 // using input stream as this works for both files in the classes directory
176 // as well as files inside jars
177 InputStream aptInputStream = resource.getInputStream();
178 // Build Html View
179 HtmlView hv = new HtmlView();
180 Map<String, String> modelMap = new HashMap<String, String>();
181 modelMap.put("title", "Name Search API");
182 // Convert Apt to Html
183 modelMap.put("body", DocUtils.convertAptToHtml(aptInputStream));
184 mv.addAllObjects(modelMap);
185 mv.setView(hv);
186
187 return mv;
188 }
189
190 /**
191 * Returns a list of scientific names matching the <code>{query}</code>
192 * string pattern. Each of these scientific names is accompanied by a list of
193 * name uuids, a list of taxon uuids, a list of accepted taxon uuids, etc.
194 * <p>
195 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
196 * <p>
197 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>
198 *
199 * @param queries
200 * The base scientific name pattern(s) to query for. The query can
201 * contain wildcard characters ('*'). The query can be
202 * performed with no wildcard or with the wildcard at the
203 * begin and / or end depending on the search pattern.
204 * @param request Http servlet request.
205 * @param response Http servlet response.
206 * @return a list of {@link NameSearch} objects each corresponding to a
207 * single query. These are built from {@link TaxonNameBase}
208 * entities which are in turn initialized using
209 * the {@link #NAME_SEARCH_INIT_STRATEGY}
210 * @throws IOException
211 */
212 @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"query"})
213 public ModelAndView doGetNameSearch(@RequestParam(value = "query", required = true) String[] queries,
214 HttpServletRequest request, HttpServletResponse response) throws IOException {
215 return doGetNameSearch(queries, DEFAULT_SEARCH_TYPE, request, response);
216 }
217
218 /**
219 * Returns a list of scientific names matching the <code>{query}</code>
220 * string pattern. Each of these scientific names is accompanied by a list of
221 * name uuids, a list of taxon uuids and a list of accepted taxon uuids.
222 * <p>
223 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-default.html">here</a>
224 * <p>
225 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>
226 *
227 * @param query
228 * The scientific name pattern(s) to query for. The query can
229 * contain wildcard characters ('*'). The query can be
230 * performed with no wildcard or with the wildcard at the
231 * begin and / or end depending on the search pattern.
232 * @param type
233 * The type of name to query. This be either
234 * "name" : scientific name corresponding to 'name cache' in CDM or
235 * "title" : complete name corresponding to 'title cache' in CDM
236 * @param request Http servlet request.
237 * @param response Http servlet response.
238 * @return a List of {@link NameSearch} objects each corresponding to a
239 * single query. These are built from {@link TaxonNameBase} entities
240 * which are in turn initialized using the {@link #NAME_SEARCH_INIT_STRATEGY}
241 * @throws IOException
242 */
243 @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"query", "type"})
244 public ModelAndView doGetNameSearch(@RequestParam(value = "query", required = true) String[] queries,
245 @RequestParam(value = "type", required = false, defaultValue = DEFAULT_SEARCH_TYPE) String searchType,
246 HttpServletRequest request, HttpServletResponse response) throws IOException {
247 ModelAndView mv = new ModelAndView();
248 List<RemoteResponse> nsList = new ArrayList<RemoteResponse>();
249
250 // if search type is not known then return error
251 if (!searchType.equals(NAME_SEARCH) && !searchType.equals(TITLE_SEARCH)) {
252 ErrorResponse er = new ErrorResponse();
253 er.setErrorMessage("searchType parameter can only be set as" + NAME_SEARCH + " or "
254 + TITLE_SEARCH);
255 mv.addObject(er);
256 return mv;
257 }
258
259 // search through each query
260 for (String query : queries) {
261
262 String queryWOWildcards = getQueryWithoutWildCards(query);
263 MatchMode mm = getMatchModeFromQuery(query);
264 logger.info("doGetNameSearch()" + request.getServletPath() + " for query \"" + query
265 + "\" without wild cards : " + queryWOWildcards + " and match mode : " + mm);
266 List<NonViralName> nameList = new ArrayList<NonViralName>();
267
268 // if "name" search then find by name cache
269 if (searchType.equals(NAME_SEARCH)) {
270 nameList = (List<NonViralName>) service.findNamesByNameCache(queryWOWildcards, mm,
271 NAME_SEARCH_INIT_STRATEGY);
272 }
273
274 //if "title" search then find by title cache
275 if (searchType.equals(TITLE_SEARCH)) {
276 nameList = (List<NonViralName>) service.findNamesByTitleCache(queryWOWildcards, mm,
277 NAME_SEARCH_INIT_STRATEGY);
278 }
279
280 // if search is successful then get related information , else return error
281 if (nameList == null || !nameList.isEmpty()) {
282 NameSearch ns = new NameSearch();
283 ns.setRequest(query);
284
285 for (NonViralName nvn : nameList) {
286 // we need to retrieve both taxon uuid of name queried and
287 // the corresponding accepted taxa.
288 // reason to return accepted taxa also, is to be able to get from
289 // scientific name to taxon concept in two web service calls.
290 Set<TaxonBase> tbSet = nvn.getTaxonBases();
291 Set<TaxonBase> accTbSet = new HashSet<TaxonBase>();
292 for (TaxonBase tb : tbSet) {
293 // if synonym then get accepted taxa.
294 if (tb instanceof Synonym) {
295 Synonym synonym = (Synonym) tb;
296 Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();
297 for (SynonymRelationship sr : synRelationships) {
298 Taxon accTaxon = sr.getAcceptedTaxon();
299 accTbSet.add(accTaxon);
300 }
301 } else {
302 accTbSet.add(tb);
303 }
304 }
305 // update name search object
306 ns.addToResponseList(nvn.getTitleCache(), nvn.getNameCache(), nvn.getUuid()
307 .toString(), tbSet, accTbSet);
308 }
309 nsList.add(ns);
310
311 } else {
312 ErrorResponse er = new ErrorResponse();
313 er.setErrorMessage("No Taxon Name for given query : " + query);
314 nsList.add(er);
315 }
316 }
317 mv.addObject(nsList);
318 return mv;
319 }
320
321
322 /**
323 * Returns a documentation page for the Name Information API.
324 * <p>
325 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/name</b>
326 *
327 * @param request Http servlet request.
328 * @param response Http servlet response.
329 * @return Html page describing the Name Information API
330 * @throws IOException
331 */
332 @RequestMapping(value = { "name" }, method = RequestMethod.GET, params = {})
333 public ModelAndView doGetNameInformationDocumentation(
334 HttpServletRequest request, HttpServletResponse response)
335 throws IOException {
336 ModelAndView mv = new ModelAndView();
337 // Read apt documentation file.
338 Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-name-info.apt");
339 // using input stream as this works for both files in the classes directory
340 // as well as files inside jars
341 InputStream aptInputStream = resource.getInputStream();
342 // Build Html View
343 HtmlView hv = new HtmlView();
344 Map<String, String> modelMap = new HashMap<String, String>();
345 modelMap.put("title", "Name Information API");
346 // Convert Apt to Html
347 modelMap.put("body", DocUtils.convertAptToHtml(aptInputStream));
348 mv.addAllObjects(modelMap);
349 mv.setView(hv);
350
351 return mv;
352 }
353
354 /**
355 * Returns information related to the scientific name matching the given
356 * <code>{nameUuid}</code>. The information includes the name string,
357 * relationships, rank, list of related lsids / taxon uuids, etc.
358 * <p>
359 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-name-info.html">here</a>
360 * <p>
361 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/name</b>
362 *
363 * @param nameUuids uuid(s) of the scientific name to search for.
364 * @param request Http servlet request.
365 * @param response Http servlet response.
366 * @return a List of {@link NameInformation} objects each corresponding to a
367 * single name uuid. These are built from {@link TaxonNameBase} entities
368 * which are in turn initialized using the {@link #NAME_INFORMATION_INIT_STRATEGY}
369 * @throws IOException
370 */
371 @RequestMapping(value = { "name" }, method = RequestMethod.GET, params = {"nameUuid"})
372 public ModelAndView doGetNameInformation(@RequestParam(value = "nameUuid", required = true) String[] nameUuids,
373 HttpServletRequest request, HttpServletResponse response) throws IOException {
374 ModelAndView mv = new ModelAndView();
375 List<RemoteResponse> niList = new ArrayList<RemoteResponse>();
376 // loop through each name uuid
377 for (String nameUuid : nameUuids) {
378 logger.info("doGetNameInformation()" + request.getServletPath() + " for name uuid \""
379 + nameUuid + "\"");
380 // find name by uuid
381 NonViralName nvn = (NonViralName) service.findNameByUuid(UUID.fromString(nameUuid),
382 NAME_INFORMATION_INIT_STRATEGY);
383
384 // if search is successful then get related information, else return error
385 if (nvn != null) {
386 NameInformation ni = new NameInformation();
387 ni.setRequest(nameUuid);
388 Reference ref = (Reference) nvn.getNomenclaturalReference();
389 String citation = "";
390 String citation_details = "";
391 if (ref != null) {
392 citation = ref.getTitleCache();
393 }
394 // update name information object
395 ni.setResponse(nvn.getTitleCache(), nvn.getNameCache(), nvn.getRank().getTitleCache(),
396 nvn.getStatus(), citation, nvn.getRelationsFromThisName(),
397 nvn.getRelationsToThisName(), nvn.getTaxonBases());
398 niList.add(ni);
399 } else {
400 ErrorResponse re = new ErrorResponse();
401 re.setErrorMessage("No Taxon Name for given UUID : " + nameUuid);
402 niList.add(re);
403 }
404 }
405 mv.addObject(niList);
406 return mv;
407 }
408
409 /**
410 * Returns a documentation page for the Taxon Information API.
411 * <p>
412 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/taxon</b>
413 *
414 * @param request Http servlet request.
415 * @param response Http servlet response.
416 * @return Html page describing the Taxon Information API
417 * @throws IOException
418 */
419 @RequestMapping(value = { "taxon" }, method = RequestMethod.GET, params = {})
420 public ModelAndView doGetTaxonInformation(
421 HttpServletRequest request, HttpServletResponse response)
422 throws IOException {
423 ModelAndView mv = new ModelAndView();
424 // Read apt documentation file.
425 Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/name-catalogue-taxon-info.apt");
426 // using input stream as this works for both files in the classes directory
427 // as well as files inside jars
428 InputStream aptInputStream = resource.getInputStream();
429 // Build Html View
430 HtmlView hv = new HtmlView();
431 Map<String, String> modelMap = new HashMap<String, String>();
432 modelMap.put("title", "Taxon Information API");
433 // Convert Apt to Html
434 modelMap.put("body", DocUtils.convertAptToHtml(aptInputStream));
435 mv.addAllObjects(modelMap);
436 mv.setView(hv);
437
438 return mv;
439 }
440
441 /**
442 * Returns information related to the taxon matching the given
443 * <code>{taxonUuid}</code>. The information includes the name cache, title cache
444 * relationship type, taxonomic status, information , etc.
445 * <p>
446 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
447 * <p>
448 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue</b>
449 *
450 * @param taxonUuid
451 * The taxon uuid to query for. The classification returned corresponds
452 * to the first in the alphabetically sorted list of classifications
453 * currently available in the database.
454 *
455 * @param request Http servlet request.
456 * @param response Http servlet response.
457 * @return a List of {@link TaxonInformation} objects each corresponding to a
458 * single query. These are built from {@TaxonBase} entities which are
459 * in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
460 * @throws IOException
461 */
462 @RequestMapping(value = { "taxon" }, method = RequestMethod.GET,params = {"taxonUuid"})
463 public ModelAndView doGetTaxonInformation(
464 @RequestParam(value = "taxonUuid", required = true) String[] taxonUuids,
465 HttpServletRequest request, HttpServletResponse response) throws IOException {
466 return doGetTaxonInformation(taxonUuids,CLASSIFICATION_DEFAULT, request, response);
467 }
468
469 /**
470 * Returns information related to the taxon matching the given
471 * <code>{taxonUuid}</code>. The information includes the name cache, title cache
472 * relationship type, taxonomic status, information , etc.
473 * <p>
474 * Endpoint documentation can be found <a href="{@docRoot}/../remote/name-catalogue-taxon-info.html">here</a>
475 * <p>
476 * URI: <b>&#x002F;{datasource-name}&#x002F;name_catalogue/taxon</b>
477 *
478 * @param taxonUuid
479 * The taxon uuid to query for.
480 * @param classification
481 * [Optional] String representing the taxonomic classification to use for
482 * building the classification tree. Defaults to the first in the alphabetically
483 * sorted list of classifications currently available in the database.
484 *
485 * @param request Http servlet request.
486 * @param response Http servlet response.
487 * @return a List of {@link TaxonInformation} objects each corresponding to a
488 * single query. These are built from {@TaxonBase} entities which are
489 * in turn initialized using the {@link #TAXON_INFORMATION_INIT_STRATEGY}
490 * @throws IOException
491 */
492 @RequestMapping(value = { "taxon" }, method = RequestMethod.GET, params = {"taxonUuid", "classification"})
493 public ModelAndView doGetTaxonInformation(
494 @RequestParam(value = "taxonUuid", required = true) String[] taxonUuids,
495 @RequestParam(value = "classification", required = false, defaultValue = CLASSIFICATION_DEFAULT) String classificationType,
496 HttpServletRequest request, HttpServletResponse response) throws IOException {
497 ModelAndView mv = new ModelAndView();
498 List<RemoteResponse> tiList = new ArrayList<RemoteResponse>();
499 // loop through each name uuid
500 for (String taxonUuid : taxonUuids) {
501 logger.info("doGetTaxonInformation()" + request.getServletPath() + " for taxon uuid \""
502 + taxonUuid);
503 // find name by uuid
504 TaxonBase tb = taxonService.findTaxonByUuid(UUID.fromString(taxonUuid),
505 TAXON_INFORMATION_INIT_STRATEGY);
506 // if search is successful then get related information, else return error
507 if (tb != null) {
508 TaxonInformation ti = new TaxonInformation();
509 ti.setRequest(taxonUuid);
510 // check if result (taxon base) is a taxon or synonym
511 if (tb.isInstanceOf(Taxon.class)) {
512 Taxon taxon = (Taxon) tb;
513 // build classification map
514 Map classificationMap = buildClassificationMap(taxon, classificationType);
515 if (classificationMap == null) {
516 ErrorResponse er = new ErrorResponse();
517 er.setErrorMessage("Invalid classification type");
518 mv.addObject(er);
519 return mv;
520 }
521
522 if (classificationMap.isEmpty()) {
523 ErrorResponse er = new ErrorResponse();
524 er.setErrorMessage("No classification available for requested type");
525 mv.addObject(er);
526 return mv;
527 }
528 // update taxon information object with taxon related data
529 ti.setResponseTaxon(tb.getTitleCache(), ACCEPTED_NAME_STATUS, buildFlagMap(tb),
530 classificationMap);
531 Set<SynonymRelationship> synRelationships = taxon.getSynonymRelations();
532 // add synonyms (if exists) to taxon information object
533 for (SynonymRelationship sr : synRelationships) {
534 Synonym syn = sr.getSynonym();
535 String uuid = syn.getUuid().toString();
536 String title = syn.getTitleCache();
537 String status = SYNONYM_STATUS;
538 String relLabel = sr.getType()
539 .getInverseRepresentation(Language.DEFAULT())
540 .getLabel();
541 ti.addToResponseRelatedTaxa(uuid, title, status, "", relLabel);
542 }
543
544 // build relationship information as,
545 // - relationships from the requested taxon
546 Set<TaxonRelationship> trFromSet = taxon.getRelationsFromThisTaxon();
547 for (TaxonRelationship tr : trFromSet) {
548 String titleTo = tr.getRelatedTo().getTitleCache();
549 String uuid = tr.getRelatedTo().getUuid().toString();
550 String status = ACCEPTED_NAME_STATUS;
551 String relLabel = tr.getType().getRepresentation(Language.DEFAULT())
552 .getLabel();
553 ti.addToResponseRelatedTaxa(uuid, titleTo, status, "", relLabel);
554 }
555
556 // - relationships from the requested taxon
557 Set<TaxonRelationship> trToSet = taxon.getRelationsToThisTaxon();
558 for (TaxonRelationship tr : trToSet) {
559 String titleFrom = tr.getRelatedFrom().getTitleCache();
560 String uuid = tr.getRelatedFrom().getUuid().toString();
561 String status = ACCEPTED_NAME_STATUS;
562 String relLabel = tr.getType()
563 .getInverseRepresentation(Language.DEFAULT())
564 .getLabel();
565 ti.addToResponseRelatedTaxa(uuid, titleFrom, status, "", relLabel);
566 }
567 } else if (tb instanceof Synonym) {
568 Synonym synonym = (Synonym) tb;
569 // update taxon information object with synonym related data
570 ti.setResponseTaxon(synonym.getTitleCache(), SYNONYM_STATUS,
571 buildFlagMap(synonym), null);
572 // add accepted taxa (if exists) to taxon information object
573 Set<SynonymRelationship> synRelationships = synonym.getSynonymRelations();
574 for (SynonymRelationship sr : synRelationships) {
575 Taxon accTaxon = sr.getAcceptedTaxon();
576 String uuid = accTaxon.getUuid().toString();
577 String title = accTaxon.getTitleCache();
578 String status = ACCEPTED_NAME_STATUS;
579 String relLabel = sr.getType().getRepresentation(Language.DEFAULT())
580 .getLabel();
581 ti.addToResponseRelatedTaxa(uuid, title, status, "", relLabel);
582 }
583 }
584 tiList.add(ti);
585 } else {
586 ErrorResponse re = new ErrorResponse();
587 re.setErrorMessage("No Taxon for given UUID : " + taxonUuid);
588 tiList.add(re);
589 }
590 }
591 mv.addObject(tiList);
592 return mv;
593 }
594
595 /**
596 * Returns the match mode by parsing the input string of wildcards.
597 *
598 * @param query
599 * String to parse.
600 *
601 * @return {@link MatchMode} depending on the the position of the wildcard (*)
602 */
603 private MatchMode getMatchModeFromQuery(String query) {
604 if (query.startsWith("*") && query.endsWith("*")) {
605 return MatchMode.ANYWHERE;
606 } else if (query.startsWith("*")) {
607 return MatchMode.END;
608 } else if (query.endsWith("*")) {
609 return MatchMode.BEGINNING;
610 } else {
611 return MatchMode.EXACT;
612 }
613 }
614
615 /**
616 * Removes wildcards from the input string.
617 *
618 * @param query
619 * String to parse.
620 *
621 * @return input string with wildcards removed
622 */
623 private String getQueryWithoutWildCards(String query) {
624
625 String newQuery = query;
626
627 if (query.startsWith("*")) {
628 newQuery = newQuery.substring(1, newQuery.length());
629 }
630
631 if (query.endsWith("*")) {
632 newQuery = newQuery.substring(0, newQuery.length() - 1);
633 }
634
635 return newQuery.trim();
636 }
637
638 /**
639 * Build map with taxon flag key-value pairs.
640 */
641 private Map<String, String> buildFlagMap(TaxonBase tb) {
642 Map<String, String> flags = new Hashtable<String, String>();
643 flags.put(DOUBTFUL_FLAG, Boolean.toString(tb.isDoubtful()));
644 return flags;
645 }
646
647 /**
648 * Build classification map.
649 */
650 private Map<String, Map> buildClassificationMap(Taxon taxon, String classificationType) {
651 // Using TreeMap is important, because we need the sorting of the classification keys
652 // in the map to be stable.
653 TreeMap<String, Map> sourceClassificationMap = new TreeMap<String, Map>();
654 Set<TaxonNode> taxonNodes = taxon.getTaxonNodes();
655 //loop through taxon nodes and build classification map for each classification key
656 for (TaxonNode tn : taxonNodes) {
657 Map<String, String> classificationMap = new LinkedHashMap<String, String>();
658 List<TaxonNode> tnList = classificationService.loadTreeBranchToTaxon(taxon,
659 tn.getClassification(), null, TAXON_NODE_INIT_STRATEGY);
660 for (TaxonNode classificationtn : tnList) {
661
662 classificationMap.put(classificationtn.getTaxon().getName().getRank().getTitleCache(),
663 classificationtn.getTaxon().getName().getTitleCache());
664 }
665
666 String cname = tn.getClassification().getTitleCache();
667 String[] words = cname.split("\\s+");
668 // "\\s+" in regular expression language meaning one or
669 // more spaces
670 StringBuilder builder = new StringBuilder();
671 for (String word : words) {
672 builder.append(word);
673 }
674 cname = builder.toString();
675 sourceClassificationMap.put(cname, classificationMap);
676 }
677
678 // if classification key is 'default' then return the first element of the map
679 if(classificationType.equals(CLASSIFICATION_DEFAULT) && !sourceClassificationMap.isEmpty()) {
680 return sourceClassificationMap.firstEntry().getValue();
681 // if classification key is provided then return the classification corresponding to the key
682 } else if(sourceClassificationMap.containsKey(classificationType)) {
683 return sourceClassificationMap.get(classificationType);
684 // if classification key is 'all' then return the entire map
685 } else if(classificationType.equals(CLASSIFICATION_ALL)) {
686 return sourceClassificationMap;
687 } else {
688 return null;
689 }
690 }
691
692 @Override
693 public void setResourceLoader(ResourceLoader resourceLoader) {
694 this.resourceLoader = resourceLoader;
695
696 }
697 }