(no commit message)
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / TaxonPortalController.java
1 // $Id: TaxonController.java 5473 2009-03-25 13:42:07Z a.kohlbecker $
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.cdm.remote.controller;
12
13 import java.io.IOException;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.HashSet;
17 import java.util.Hashtable;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.NoSuchElementException;
21 import java.util.Set;
22 import java.util.SortedMap;
23 import java.util.TreeMap;
24 import java.util.UUID;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27
28 import javax.servlet.http.HttpServletRequest;
29 import javax.servlet.http.HttpServletResponse;
30
31 import org.apache.commons.lang.ObjectUtils;
32 import org.apache.log4j.Logger;
33 import org.springframework.beans.factory.annotation.Autowired;
34 import org.springframework.stereotype.Controller;
35 import org.springframework.web.bind.WebDataBinder;
36 import org.springframework.web.bind.annotation.InitBinder;
37 import org.springframework.web.bind.annotation.RequestMapping;
38 import org.springframework.web.bind.annotation.RequestMethod;
39 import org.springframework.web.bind.annotation.RequestParam;
40 import org.springframework.web.servlet.ModelAndView;
41
42 import eu.etaxonomy.cdm.api.service.IDescriptionService;
43 import eu.etaxonomy.cdm.api.service.INameService;
44 import eu.etaxonomy.cdm.api.service.IReferenceService;
45 import eu.etaxonomy.cdm.api.service.ITaxonService;
46 import eu.etaxonomy.cdm.api.service.ITaxonTreeService;
47 import eu.etaxonomy.cdm.api.service.config.ITaxonServiceConfigurator;
48 import eu.etaxonomy.cdm.api.service.config.impl.TaxonServiceConfiguratorImpl;
49 import eu.etaxonomy.cdm.api.service.pager.Pager;
50 import eu.etaxonomy.cdm.database.UpdatableRoutingDataSource;
51 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
52 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
53 import eu.etaxonomy.cdm.model.description.TaxonDescription;
54 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
55 import eu.etaxonomy.cdm.model.location.NamedArea;
56 import eu.etaxonomy.cdm.model.media.Media;
57 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
58 import eu.etaxonomy.cdm.model.name.NameRelationship;
59 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
60 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
61 import eu.etaxonomy.cdm.model.taxon.Synonym;
62 import eu.etaxonomy.cdm.model.taxon.Taxon;
63 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
64 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
65 import eu.etaxonomy.cdm.model.taxon.TaxonRelationshipType;
66 import eu.etaxonomy.cdm.model.taxon.TaxonomicTree;
67 import eu.etaxonomy.cdm.persistence.query.MatchMode;
68 import eu.etaxonomy.cdm.remote.editor.NamedAreaPropertyEditor;
69 import eu.etaxonomy.cdm.remote.editor.UUIDPropertyEditor;
70
71 /**
72 * The TaxonPortalController class is a Spring MVC Controller.
73 * <p>
74 * The syntax of the mapped service URIs contains the the {datasource-name} path element.
75 * The available {datasource-name}s are defined in a configuration file which
76 * is loaded by the {@link UpdatableRoutingDataSource}. If the
77 * UpdatableRoutingDataSource is not being used in the actual application
78 * context any arbitrary {datasource-name} may be used.
79 * <p>
80 * Methods mapped at type level, inherited from super classes ({@link BaseController}):
81 * <blockquote>
82 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}</b>
83 *
84 * Get the {@link TaxonBase} instance identified by the <code>{taxon-uuid}</code>.
85 * The returned Taxon is initialized by
86 * the following strategy {@link #TAXON_INIT_STRATEGY}
87 * </blockquote>
88 *
89 * @author a.kohlbecker
90 * @date 20.07.2009
91 *
92 */
93 @Controller
94 @RequestMapping(value = {"/*/portal/taxon/*", "/*/portal/taxon/*/*", "/*/portal/name/*/*", "/*/portal/taxon/*/media/*/*"})
95 public class TaxonPortalController extends BaseController<TaxonBase, ITaxonService>
96 {
97 public static final Logger logger = Logger.getLogger(TaxonPortalController.class);
98
99 @Autowired
100 private INameService nameService;
101 @Autowired
102 private IDescriptionService descriptionService;
103 @Autowired
104 private IReferenceService referenceService;
105
106 @Autowired
107 private ITaxonTreeService taxonTreeService;
108
109 private static final List<String> TAXON_INIT_STRATEGY = Arrays.asList(new String []{
110 "*",
111 // taxon relations
112 "relationsToThisName.fromTaxon.name.taggedName",
113 // the name
114 "name.$",
115 "name.taggedName",
116 "name.rank.representations",
117 "name.status.type.representations",
118
119 // taxon descriptions
120 "descriptions.elements.$",
121 "descriptions.elements.area",
122 "descriptions.elements.area.$",
123 "descriptions.elements.multilanguageText",
124 "descriptions.elements.media.representations.parts",
125
126 // // typeDesignations
127 // "name.typeDesignations.$",
128 // "name.typeDesignations.citation.authorTeam",
129 // "name.typeDesignations.typeName.$",
130 // "name.typeDesignations.typeStatus.representations",
131 // "name.typeDesignations.typeSpecimen.media.representations.parts"
132
133 });
134
135 private static final List<String> SIMPLE_TAXON_INIT_STRATEGY = Arrays.asList(new String []{
136 "*",
137 // taxon relations
138 "relationsToThisName.fromTaxon.name.taggedName",
139 // the name
140 "name.$",
141 "name.taggedName",
142 "name.rank.representations",
143 "name.status.type.representations"
144 });
145
146 private static final List<String> SYNONYMY_INIT_STRATEGY = Arrays.asList(new String []{
147 // initialize homotypical and heterotypical groups; needs synonyms
148 "synonymRelations.$",
149 "synonymRelations.synonym.$",
150 "synonymRelations.synonym.name.taggedName",
151 "synonymRelations.synonym.name.nomenclaturalReference.inBook.authorTeam",
152 "synonymRelations.synonym.name.nomenclaturalReference.inJournal",
153 "synonymRelations.synonym.name.nomenclaturalReference.inProceedings",
154 "synonymRelations.synonym.name.homotypicalGroup.typifiedNames.$",
155 "synonymRelations.synonym.name.homotypicalGroup.typifiedNames.name.taggedName",
156 "synonymRelations.synonym.name.homotypicalGroup.typifiedNames.taxonBases.$",
157 "synonymRelations.synonym.name.homotypicalGroup.typifiedNames.taxonBases.name.taggedName",
158
159 "name.homotypicalGroup.$",
160 "name.homotypicalGroup.typifiedNames.$",
161 "name.homotypicalGroup.typifiedNames.name.taggedName",
162
163 "name.homotypicalGroup.typifiedNames.taxonBases.$",
164 "name.homotypicalGroup.typifiedNames.taxonBases.name.taggedName"
165
166 });
167
168 private static final List<String> TAXONRELATIONSHIP_INIT_STRATEGY = Arrays.asList(new String []{
169 "$",
170 "type.inverseRepresentations",
171 "fromTaxon.sec.authorTeam",
172 "fromTaxon.name.taggedName"
173 });
174
175 private static final List<String> NAMERELATIONSHIP_INIT_STRATEGY = Arrays.asList(new String []{
176 "$",
177 "type.inverseRepresentations",
178 "fromName.taggedName",
179 });
180
181
182 protected static final List<String> TAXONDESCRIPTION_INIT_STRATEGY = Arrays.asList(new String []{
183 "$",
184 "elements.$",
185 "elements.sources.citation.",
186 "elements.sources.citation.authorTeam.$",
187 //"elements.sources.citation.authorTeam.teamMembers.",
188 "elements.multilanguageText",
189 "elements.media.representations.parts",
190 });
191
192 private static final List<String> NAMEDESCRIPTION_INIT_STRATEGY = Arrays.asList(new String []{
193 "uuid",
194 "feature",
195 "elements.$",
196 "elements.multilanguageText",
197 "elements.media.representations.parts",
198 });
199
200 private static final List<String> TYPEDESIGNATION_INIT_STRATEGY = Arrays.asList(new String []{
201 //"$",
202 "typeSpecimen.$",
203 "typeStatus.representations",
204 "citation.authorTeam",
205 "typeName.taggedName"
206 });
207
208
209
210 private static final String featureTreeUuidPattern = "^/(?:[^/]+)/taxon(?:(?:/)([^/?#&\\.]+))+.*";
211
212 public TaxonPortalController(){
213 super();
214 setInitializationStrategy(TAXON_INIT_STRATEGY);
215 setUuidParameterPattern("^/(?:[^/]+)/portal/(?:[^/]+)/([^/?#&\\.]+).*");
216 }
217
218 /* (non-Javadoc)
219 * @see eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu.etaxonomy.cdm.api.service.IService)
220 */
221 @Autowired
222 @Override
223 public void setService(ITaxonService service) {
224 this.service = service;
225 }
226
227 @InitBinder
228 public void initBinder(WebDataBinder binder) {
229 binder.registerCustomEditor(UUID.class, new UUIDPropertyEditor());
230 binder.registerCustomEditor(NamedArea.class, new NamedAreaPropertyEditor());
231 }
232
233
234 /* (non-Javadoc)
235 * @see eu.etaxonomy.cdm.remote.controller.BaseController#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
236
237 @Override
238 @RequestMapping(method = RequestMethod.GET)
239 public TaxonBase doGet(HttpServletRequest request, HttpServletResponse response)throws IOException {
240 logger.info("doGet()");
241 TaxonBase tb = getCdmBase(request, response, TAXON_INIT_STRATEGY, TaxonBase.class);
242 return tb;
243 }
244 */
245 /**
246 * Find Taxa, Synonyms, Common Names by name, either globally or in a specific geographic area.
247 * <p>
248 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;find</b>
249 *
250 * @param query
251 * the string to query for. Since the wildcard character '*'
252 * internally always is appended to the query string, a search
253 * always compares the query string with the beginning of a name.
254 * - <i>required parameter</i>
255 * @param treeUuid
256 * the {@link UUID} of a {@link TaxonomicTree} to which the
257 * search is to be restricted. - <i>optional parameter</i>
258 * @param areas
259 * restrict the search to a set of geographic {@link NamedArea}s.
260 * The parameter currently takes a list of TDWG area labels.
261 * - <i>optional parameter</i>
262 * @param page
263 * the number of the page to be returned, the first page has the
264 * pageNumber = 1 - <i>optional parameter</i>
265 * @param pageSize
266 * the maximum number of entities returned per page (can be null
267 * to return all entities in a single page) - <i>optional parameter</i>
268 * @param doTaxa
269 * weather to search for instances of {@link Taxon} - <i>optional parameter</i>
270 * @param doSynonyms
271 * weather to search for instances of {@link Synonym} - <i>optional parameter</i>
272 * @param doTaxaByCommonNames
273 * for instances of {@link Taxon} by a common name used - <i>optional parameter</i>
274 * @return a Pager on a list of {@link IdentifiableEntity}s initialized by
275 * the following strategy {@link #SIMPLE_TAXON_INIT_STRATEGY}
276 * @throws IOException
277 */
278 @RequestMapping(method = RequestMethod.GET,
279 value = {"/*/portal/taxon/find"}) //TODO map to path /*/portal/taxon/
280 public Pager<IdentifiableEntity> doFind(
281 @RequestParam(value = "query", required = false) String query,
282 @RequestParam(value = "tree", required = false) UUID treeUuid,
283 @RequestParam(value = "area", required = false) Set<NamedArea> areas,
284 @RequestParam(value = "page", required = false) Integer page,
285 @RequestParam(value = "pageSize", required = false) Integer pageSize,
286 @RequestParam(value = "doTaxa", required = false) Boolean doTaxa,
287 @RequestParam(value = "doSynonyms", required = false) Boolean doSynonyms,
288 @RequestParam(value = "doTaxaByCommonNames", required = false) Boolean doTaxaByCommonNames)
289 throws IOException {
290
291 logger.info("doFind( " +
292 "query=\"" + ObjectUtils.toString(query) + "\", treeUuid=" + ObjectUtils.toString(treeUuid) +
293 ", area=" + ObjectUtils.toString(areas) +
294 ", pageSize=" + ObjectUtils.toString(pageSize) + ", page=" + ObjectUtils.toString(page) +
295 ", doTaxa=" + ObjectUtils.toString(doTaxa) + ", doSynonyms=" + ObjectUtils.toString(doSynonyms) +")" );
296
297 if(page == null){ page = BaseListController.DEFAULT_PAGE_NUMBER;}
298 if(pageSize == null){ pageSize = BaseListController.DEFAULT_PAGESIZE;}
299
300 ITaxonServiceConfigurator config = new TaxonServiceConfiguratorImpl();
301 config.setPageNumber(page);
302 config.setPageSize(pageSize);
303 config.setSearchString(query);
304 config.setDoTaxa(doTaxa!= null ? doTaxa : Boolean.FALSE );
305 config.setDoSynonyms(doSynonyms != null ? doSynonyms : Boolean.FALSE );
306 config.setDoTaxaByCommonNames(doTaxaByCommonNames != null ? doTaxaByCommonNames : Boolean.FALSE );
307 config.setMatchMode(MatchMode.BEGINNING);
308 config.setTaxonPropertyPath(SIMPLE_TAXON_INIT_STRATEGY);
309 config.setNamedAreas(areas);
310 if(treeUuid != null){
311 TaxonomicTree taxonomicTree = taxonTreeService.find(treeUuid);
312 config.setTaxonomicTree(taxonomicTree);
313 }
314
315 return (Pager<IdentifiableEntity>) service.findTaxaAndNames(config);
316 }
317
318 /**
319 * Get the synonymy for a taxon identified by the <code>{taxon-uuid}</code>.
320 * The synonymy consists
321 * of two parts: The group of homotypic synonyms of the taxon and the
322 * heterotypic synonymy groups of the taxon. The synonymy is ordered
323 * historically by the type designations and by the publication date of the
324 * nomenclatural reference
325 * <p>
326 * URI:
327 * <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;synonymy</b>
328 *
329 *
330 * @param request
331 * @param response
332 * @return a Map with to entries which are mapped by the following keys:
333 * "homotypicSynonymsByHomotypicGroup", "heterotypicSynonymyGroups",
334 * containing lists of {@link Synonym}s which are initialized using the
335 * following initialization strategy: {@link #SYNONYMY_INIT_STRATEGY}
336 *
337 * @throws IOException
338 */
339 @RequestMapping(
340 value = {"/*/portal/taxon/*/synonymy"},
341 method = RequestMethod.GET)
342 public ModelAndView doGetSynonymy(HttpServletRequest request, HttpServletResponse response)throws IOException {
343
344 logger.info("doGetSynonymy() " + request.getServletPath());
345 ModelAndView mv = new ModelAndView();
346 TaxonBase tb = getCdmBase(request, response, null, Taxon.class);
347 Taxon taxon = (Taxon)tb;
348 Map<String, List<?>> synonymy = new Hashtable<String, List<?>>();
349 synonymy.put("homotypicSynonymsByHomotypicGroup", service.getHomotypicSynonymsByHomotypicGroup(taxon, SYNONYMY_INIT_STRATEGY));
350 synonymy.put("heterotypicSynonymyGroups", service.getHeterotypicSynonymyGroups(taxon, SYNONYMY_INIT_STRATEGY));
351 mv.addObject(synonymy);
352 return mv;
353 }
354
355 /**
356 * Get the set of accepted {@link Taxon} entities for a given
357 * {@link TaxonBase} entity identified by the <code>{taxon-uuid}</code>.
358 * <p>
359 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;accepted</b>
360 *
361 * @param request
362 * @param response
363 * @return a Set of {@link Taxon} entities which are initialized
364 * using the following initialization strategy:
365 * {@link #SYNONYMY_INIT_STRATEGY}
366 * @throws IOException
367 */
368 @RequestMapping(value = "/*/portal/taxon/*/accepted", method = RequestMethod.GET)
369 public Set<TaxonBase> getAccepted(HttpServletRequest request, HttpServletResponse response) throws IOException {
370
371 logger.info("getAccepted() " + request.getServletPath());
372
373 UUID uuid = readValueUuid(request, null);
374 TaxonBase tb = service.load(uuid, SYNONYMY_INIT_STRATEGY);
375 if(tb == null){
376 response.sendError(HttpServletResponse.SC_NOT_FOUND, "A taxon with the uuid " + uuid + " does not exist");
377 return null;
378 }
379 HashSet<TaxonBase> resultset = new HashSet<TaxonBase>();
380 if(tb instanceof Taxon){
381 //the taxon already is accepted
382 //FIXME take the current view into account once views are implemented!!!
383 resultset.add((Taxon)tb);
384 } else {
385 Synonym syn = (Synonym)tb;
386 for(TaxonBase accepted : syn.getAcceptedTaxa()){
387 accepted = service.load(accepted.getUuid(), SIMPLE_TAXON_INIT_STRATEGY);
388 resultset.add(accepted);
389 }
390 }
391 return resultset;
392 }
393
394 /**
395 * Get the list of {@link TaxonRelationship}s for the given
396 * {@link TaxonBase} instance identified by the <code>{taxon-uuid}</code>.
397 * <p>
398 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;taxonRelationships</b>
399 *
400 * @param request
401 * @param response
402 * @return a List of {@link TaxonRelationship} entities which are initialized
403 * using the following initialization strategy:
404 * {@link #TAXONRELATIONSHIP_INIT_STRATEGY}
405 * @throws IOException
406 */
407 @RequestMapping(
408 value = {"/*/portal/taxon/*/taxonRelationships"},
409 method = RequestMethod.GET)
410 public List<TaxonRelationship> doGetTaxonRelations(HttpServletRequest request, HttpServletResponse response)throws IOException {
411
412 logger.info("doGetTaxonRelations()" + request.getServletPath());
413 TaxonBase tb = getCdmBase(request, response, null, Taxon.class);
414 Taxon taxon = (Taxon)tb;
415 List<TaxonRelationship> relations = new ArrayList<TaxonRelationship>();
416 List<TaxonRelationship> results = service.listToTaxonRelationships(taxon, TaxonRelationshipType.MISAPPLIED_NAME_FOR(), null, null, null, TAXONRELATIONSHIP_INIT_STRATEGY);
417 relations.addAll(results);
418 results = service.listToTaxonRelationships(taxon, TaxonRelationshipType.INVALID_DESIGNATION_FOR(), null, null, null, TAXONRELATIONSHIP_INIT_STRATEGY);
419 relations.addAll(results);
420
421 return relations;
422 }
423
424 /**
425 * Get the list of {@link NameRelationship}s of the Name associated with the
426 * {@link TaxonBase} instance identified by the <code>{taxon-uuid}</code>.
427 * <p>
428 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;nameRelationships</b>
429 *
430 * @param request
431 * @param response
432 * @return a List of {@link NameRelationship} entities which are initialized
433 * using the following initialization strategy:
434 * {@link #NAMERELATIONSHIP_INIT_STRATEGY}
435 * @throws IOException
436 */
437 @RequestMapping(
438 value = {"/*/portal/taxon/*/nameRelationships"},
439 method = RequestMethod.GET)
440 public List<NameRelationship> doGetNameRelations(HttpServletRequest request, HttpServletResponse response)throws IOException {
441 logger.info("doGetNameRelations()" + request.getServletPath());
442 TaxonBase tb = getCdmBase(request, response, SIMPLE_TAXON_INIT_STRATEGY, Taxon.class);
443 List<NameRelationship> list = nameService.listToNameRelationships(tb.getName(), null, null, null, null, NAMERELATIONSHIP_INIT_STRATEGY);
444 return list;
445 }
446
447 /**
448 * Get the list of {@link TaxonNameDescription}s of the Name associated with the
449 * {@link TaxonNameBase} instance identified by the <code>{name-uuid}</code>.
450 * <p>
451 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;name&#x002F;{name-uuid}&#x002F;descriptions</b>
452 *
453 * @param request
454 * @param response
455 * @return a List of {@link TaxonNameDescription} entities which are initialized
456 * using the following initialization strategy:
457 * {@link #NAMEDESCRIPTION_INIT_STRATEGY}
458 * @throws IOException
459 */
460 @RequestMapping(
461 value = {"/*/portal/name/*/descriptions"},
462 method = RequestMethod.GET)
463 public List<TaxonNameDescription> doGetNameDescriptions(HttpServletRequest request, HttpServletResponse response)throws IOException {
464 logger.info("doGetNameDescriptions()" + request.getServletPath());
465 UUID nameUuuid = readValueUuid(request, null);
466 TaxonNameBase tnb = nameService.load(nameUuuid, null);
467 Pager<TaxonNameDescription> p = descriptionService.getTaxonNameDescriptions(tnb, null, null, NAMEDESCRIPTION_INIT_STRATEGY);
468 return p.getRecords();
469 }
470
471 /**
472 * Get the list of {@link TypeDesignationBase}s of the
473 * {@link TaxonBase} instance identified by the <code>{taxon-uuid}</code>.
474 * <p>
475 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;nameTypeDesignations</b>
476 *
477 * @param request
478 * @param response
479 * @return a List of {@link TypeDesignationBase} entities which are initialized
480 * using the following initialization strategy:
481 * {@link #TYPEDESIGNATION_INIT_STRATEGY}
482 * @throws IOException
483 */
484 @RequestMapping(
485 value = {"/*/portal/taxon/*/nameTypeDesignations"},
486 method = RequestMethod.GET)
487 public List<TypeDesignationBase> doGetNameTypeDesignations(HttpServletRequest request, HttpServletResponse response)throws IOException {
488 logger.info("doGetNameTypeDesignations()" + request.getServletPath());
489 TaxonBase tb = getCdmBase(request, response, SIMPLE_TAXON_INIT_STRATEGY, Taxon.class);
490 Pager<TypeDesignationBase> p = nameService.getTypeDesignations(tb.getName(), null, null, null, TYPEDESIGNATION_INIT_STRATEGY);
491 return p.getRecords();
492 }
493
494 /**
495 * Get the list of {@link TaxonDescription}s of the
496 * {@link Taxon} instance identified by the <code>{taxon-uuid}</code>.
497 * <p>
498 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;descriptions</b>
499 *
500 * @param request
501 * @param response
502 * @return a List of {@link TaxonDescription} entities which are initialized
503 * using the following initialization strategy:
504 * {@link #TAXONDESCRIPTION_INIT_STRATEGY}
505 * @throws IOException
506 */
507 @RequestMapping(
508 value = {"/*/portal/taxon/*/descriptions"},
509 method = RequestMethod.GET)
510 public List<TaxonDescription> doGetDescriptions(HttpServletRequest request, HttpServletResponse response)throws IOException {
511 logger.info("doGetDescriptions()" + request.getServletPath());
512 Taxon t = getCdmBase(request, response, null, Taxon.class);
513 Pager<TaxonDescription> p = descriptionService.getTaxonDescriptions(t, null, null, null, null,
514 TAXONDESCRIPTION_INIT_STRATEGY);
515 return p.getRecords();
516 }
517
518 /**
519 * Get the {@link Media} attached to the {@link Taxon} instance
520 * identified by the <code>{taxon-uuid}</code>.
521 *
522 * Usage &#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-
523 * uuid}&#x002F;media&#x002F;{mime type
524 * list}&#x002F;{size}[,[widthOrDuration}][,{height}]&#x002F;
525 *
526 * Whereas
527 * <ul>
528 * <li><b>{mime type list}</b>: a comma separated list of mime types, in the
529 * order of preference. The forward slashes contained in the mime types must
530 * be replaced by a colon. Regular expressions can be used. Each media
531 * associated with this given taxon is being searched whereas the first
532 * matching mime type matching a representation always rules.</li>
533 * <li><b>{size},{widthOrDuration},{height}</b>: <i>not jet implemented</i>
534 * valid values are an integer or the asterisk '*' as a wildcard</li>
535 * </ul>
536 *
537 * @param request
538 * @param response
539 * @return a List of {@link Media} entities which are initialized
540 * using the following initialization strategy:
541 * {@link #TAXONDESCRIPTION_INIT_STRATEGY}
542 * @throws IOException
543 */
544 @RequestMapping(
545 value = {"/*/portal/taxon/*/media/*/*"},
546 method = RequestMethod.GET)
547 public List<Media> doGetMedia(HttpServletRequest request, HttpServletResponse response)throws IOException {
548 logger.info("doGetMedia()" + request.getServletPath());
549 Taxon t = getCdmBase(request, response, null, Taxon.class);
550 Pager<TaxonDescription> p =
551 descriptionService.getTaxonDescriptions(t, null, null, null, null, TAXONDESCRIPTION_INIT_STRATEGY);
552
553 // pars the media and quality parameters
554
555
556 // collect all media of the given taxon
557 boolean limitToGalleries = false;
558 List<Media> taxonMedia = new ArrayList<Media>();
559 for(TaxonDescription desc : p.getRecords()){
560 if(!limitToGalleries || desc.isImageGallery()){
561 for(DescriptionElementBase element : desc.getElements()){
562 for(Media media : element.getMedia()){
563 taxonMedia.add(media);
564 }
565 }
566 }
567 }
568
569 // move into media ...
570
571 // find best matching representations of each media
572 String path = request.getServletPath();
573 String[] pathTokens = path.split("/");
574 String[] mimeTypes = pathTokens[6].split(",");
575 String[] sizeTokens = pathTokens[7].split(",");
576 Integer widthOrDuration = null;
577 Integer height = null;
578 Integer size = null;
579
580 for(int i=0; i<mimeTypes.length; i++){
581 mimeTypes[i] = mimeTypes[i].replace(':', '/');
582 }
583
584 if(sizeTokens.length > 0){
585 try {
586 size = Integer.valueOf(sizeTokens[0]);
587 } catch (NumberFormatException nfe) {
588 /* IGNORE */
589 }
590 }
591 if(sizeTokens.length > 1){
592 try {
593 widthOrDuration = Integer.valueOf(sizeTokens[1]);
594 } catch (NumberFormatException nfe) {
595 /* IGNORE */
596 }
597 }
598 if(sizeTokens.length > 2){
599 try {
600 height = Integer.valueOf(sizeTokens[2]);
601 } catch (NumberFormatException nfe) {
602 /* IGNORE */
603 }
604 }
605
606 List<Media> returnMedia = new ArrayList<Media>(taxonMedia.size());
607 for(Media media : taxonMedia){
608 SortedMap<String, MediaRepresentation> prefRepresentations
609 = orderMediaRepresentations(media, mimeTypes, size, widthOrDuration, height);
610 try {
611 // take first one and remove all other representations
612 MediaRepresentation prefOne = prefRepresentations.get(prefRepresentations.firstKey());
613 for (MediaRepresentation representation : media.getRepresentations()) {
614 if (representation != prefOne) {
615 media.removeRepresentation(representation);
616 }
617 }
618 returnMedia.add(media);
619 } catch (NoSuchElementException nse) {
620 /* IGNORE */
621 }
622 }
623
624 return returnMedia;
625 }
626
627 /**
628 * @param media
629 * @param mimeTypeRegexes
630 * @param size
631 * @param widthOrDuration
632 * @param height
633 * @return
634 *
635 * TODO move into a media utils class
636 * TODO implement the quality filter
637 */
638 private SortedMap<String, MediaRepresentation> orderMediaRepresentations(Media media, String[] mimeTypeRegexes,
639 Integer size, Integer widthOrDuration, Integer height) {
640 SortedMap<String, MediaRepresentation> prefRepr = new TreeMap<String, MediaRepresentation>();
641 for (String mimeTypeRegex : mimeTypeRegexes) {
642 // getRepresentationByMimeType
643 Pattern mimeTypePattern = Pattern.compile(mimeTypeRegex);
644 int representationCnt = 0;
645 for (MediaRepresentation representation : media.getRepresentations()) {
646 Matcher mather = mimeTypePattern.matcher(representation.getMimeType());
647 if (mather.matches()) {
648 int dwa = 0;
649
650 /* TODO the quality filter part is being skipped
651 * // look for representation with the best matching parts
652 for (MediaRepresentationPart part : representation.getParts()) {
653 if (part instanceof ImageFile) {
654 ImageFile image = (ImageFile) part;
655 int dw = image.getWidth() * image.getHeight() - height * widthOrDuration;
656 if (dw < 0) {
657 dw *= -1;
658 }
659 dwa += dw;
660 }
661 dwa = (representation.getParts().size() > 0 ? dwa / representation.getParts().size() : 0);
662 }*/
663 prefRepr.put((dwa + representationCnt++) + '_' + representation.getMimeType(), representation);
664
665 // preferred mime type found => end loop
666 break;
667 }
668 }
669 }
670 return prefRepr;
671 }
672
673 // ---------------------- code snippet preserved for possible later use --------------------
674 // @RequestMapping(
675 // value = {"/*/portal/taxon/*/descriptions"},
676 // method = RequestMethod.GET)
677 // public List<TaxonDescription> doGetDescriptionsbyFeatureTree(HttpServletRequest request, HttpServletResponse response)throws IOException {
678 // TaxonBase tb = getCdmBase(request, response, null, Taxon.class);
679 // if(tb instanceof Taxon){
680 // //T O D O this is a quick and dirty implementation -> generalize
681 // UUID featureTreeUuid = readValueUuid(request, featureTreeUuidPattern);
682 //
683 // FeatureTree featureTree = descriptionService.getFeatureTreeByUuid(featureTreeUuid);
684 // Pager<TaxonDescription> p = descriptionService.getTaxonDescriptions((Taxon)tb, null, null, null, null, TAXONDESCRIPTION_INIT_STRATEGY);
685 // List<TaxonDescription> descriptions = p.getRecords();
686 //
687 // if(!featureTree.isDescriptionSeparated()){
688 //
689 // TaxonDescription superDescription = TaxonDescription.NewInstance();
690 // //put all descriptionElements in superDescription and make it invisible
691 // for(TaxonDescription description: descriptions){
692 // for(DescriptionElementBase element: description.getElements()){
693 // superDescription.addElement(element);
694 // }
695 // }
696 // List<TaxonDescription> separatedDescriptions = new ArrayList<TaxonDescription>(descriptions.size());
697 // separatedDescriptions.add(superDescription);
698 // return separatedDescriptions;
699 // }else{
700 // return descriptions;
701 // }
702 // } else {
703 // response.sendError(HttpServletResponse.SC_NOT_FOUND, "invalid type; Taxon expected but " + tb.getClass().getSimpleName() + " found.");
704 // return null;
705 // }
706 // }
707
708 }