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