1 // $Id: TaxonController.java 5473 2009-03-25 13:42:07Z a.kohlbecker $
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
11 package eu
.etaxonomy
.cdm
.remote
.controller
;
13 import java
.io
.IOException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Arrays
;
16 import java
.util
.Hashtable
;
17 import java
.util
.List
;
19 import java
.util
.NoSuchElementException
;
21 import java
.util
.SortedMap
;
22 import java
.util
.TreeMap
;
23 import java
.util
.UUID
;
24 import java
.util
.regex
.Matcher
;
25 import java
.util
.regex
.Pattern
;
28 import javax
.servlet
.http
.HttpServletRequest
;
29 import javax
.servlet
.http
.HttpServletResponse
;
31 import org
.apache
.log4j
.Logger
;
32 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
33 import org
.springframework
.stereotype
.Controller
;
34 import org
.springframework
.web
.bind
.WebDataBinder
;
35 import org
.springframework
.web
.bind
.annotation
.InitBinder
;
36 import org
.springframework
.web
.bind
.annotation
.RequestMapping
;
37 import org
.springframework
.web
.bind
.annotation
.RequestMethod
;
38 import org
.springframework
.web
.bind
.annotation
.RequestParam
;
39 import org
.springframework
.web
.servlet
.ModelAndView
;
41 import eu
.etaxonomy
.cdm
.api
.service
.IDescriptionService
;
42 import eu
.etaxonomy
.cdm
.api
.service
.INameService
;
43 import eu
.etaxonomy
.cdm
.api
.service
.IReferenceService
;
44 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonService
;
45 import eu
.etaxonomy
.cdm
.api
.service
.config
.ITaxonServiceConfigurator
;
46 import eu
.etaxonomy
.cdm
.api
.service
.config
.impl
.TaxonServiceConfiguratorImpl
;
47 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
48 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
49 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
50 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
51 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
52 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
53 import eu
.etaxonomy
.cdm
.model
.media
.ImageFile
;
54 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
55 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
56 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentationPart
;
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
.occurrence
.Collection
;
61 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceBase
;
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
;
72 * @author a.kohlbecker
77 @RequestMapping(value
= {"/*/portal/taxon/*", "/*/portal/taxon/*/*", "/*/portal/name/*/*", "/*/portal/taxon/*/media/*/*"})
78 public class TaxonPortalController
extends BaseController
<TaxonBase
, ITaxonService
>
80 public static final Logger logger
= Logger
.getLogger(TaxonPortalController
.class);
83 private INameService nameService
;
85 private IDescriptionService descriptionService
;
87 private IReferenceService referenceService
;
90 private static final List
<String
> TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[]{
93 "relationsToThisName.fromTaxon.name.taggedName",
97 "name.rank.representations",
98 "name.status.type.representations",
100 // taxon descriptions
101 "descriptions.elements.$",
102 "descriptions.elements.area",
103 "descriptions.elements.area.$",
104 "descriptions.elements.multilanguageText",
105 "descriptions.elements.media.representations.parts",
107 // // typeDesignations
108 // "name.typeDesignations.$",
109 // "name.typeDesignations.citation.authorTeam",
110 // "name.typeDesignations.typeName.$",
111 // "name.typeDesignations.typeStatus.representations",
112 // "name.typeDesignations.typeSpecimen.media.representations.parts"
116 private static final List
<String
> SIMPLE_TAXON_INIT_STRATEGY
= Arrays
.asList(new String
[]{
119 "relationsToThisName.fromTaxon.name.taggedName",
123 "name.rank.representations",
124 "name.status.type.representations"
127 private static final List
<String
> SYNONYMY_INIT_STRATEGY
= Arrays
.asList(new String
[]{
128 // initialize homotypical and heterotypical groups; needs synonyms
129 "synonymRelations.$",
130 "synonymRelations.synonym.$",
131 "synonymRelations.synonym.name.taggedName",
132 "synonymRelations.synonym.name.homotypicalGroup.typifiedNames.$",
133 "synonymRelations.synonym.name.homotypicalGroup.typifiedNames.name.taggedName",
134 "synonymRelations.synonym.name.homotypicalGroup.typifiedNames.taxonBases.$",
135 "synonymRelations.synonym.name.homotypicalGroup.typifiedNames.taxonBases.name.taggedName",
137 "name.homotypicalGroup.$",
138 "name.homotypicalGroup.typifiedNames.$",
139 "name.homotypicalGroup.typifiedNames.name.taggedName",
140 "name.homotypicalGroup.typifiedNames.taxonBases.$",
141 "name.homotypicalGroup.typifiedNames.taxonBases.name.taggedName"
144 private static final List
<String
> TAXONRELATIONSHIP_INIT_STRATEGY
= Arrays
.asList(new String
[]{
146 "type.inverseRepresentations",
147 "fromTaxon.sec.authorTeam",
148 "fromTaxon.name.taggedName"
151 private static final List
<String
> NAMERELATIONSHIP_INIT_STRATEGY
= Arrays
.asList(new String
[]{
153 "type.inverseRepresentations",
154 "fromName.taggedName",
158 protected static final List
<String
> TAXONDESCRIPTION_INIT_STRATEGY
= Arrays
.asList(new String
[]{
161 "elements.citation.authorTeam",
162 "elements.multilanguageText",
163 "elements.media.representations.parts",
166 private static final List
<String
> NAMEDESCRIPTION_INIT_STRATEGY
= Arrays
.asList(new String
[]{
171 "elements.multilanguageText",
172 "elements.media.representations.parts",
175 private static final List
<String
> TYPEDESIGNATION_INIT_STRATEGY
= Arrays
.asList(new String
[]{
178 "typeStatus.representations",
179 "citation.authorTeam",
180 "typeName.taggedName"
185 private static final String featureTreeUuidPattern
= "^/(?:[^/]+)/taxon(?:(?:/)([^/?#&\\.]+))+.*";
187 public TaxonPortalController(){
189 setUuidParameterPattern("^/(?:[^/]+)/portal/(?:[^/]+)/([^/?#&\\.]+).*");
193 * @see eu.etaxonomy.cdm.remote.controller.GenericController#setService(eu.etaxonomy.cdm.api.service.IService)
197 public void setService(ITaxonService service
) {
198 this.service
= service
;
202 public void initBinder(WebDataBinder binder
) {
203 binder
.registerCustomEditor(UUID
.class, new UUIDPropertyEditor());
204 binder
.registerCustomEditor(NamedArea
.class, new NamedAreaPropertyEditor());
209 @RequestMapping(method
= RequestMethod
.GET
)
210 public TaxonBase
doGet(HttpServletRequest request
, HttpServletResponse response
)throws IOException
{
211 TaxonBase tb
= getCdmBase(request
, response
, TAXON_INIT_STRATEGY
, TaxonBase
.class);
215 @RequestMapping(method
= RequestMethod
.GET
,
216 value
= {"/*/portal/taxon/find"}) //TODO map to path /*/portal/taxon/
217 //FIXME duplicate method see TaxonPortalListController.doFind() : TaxonPortalListController is disabled!
218 public Pager
<IdentifiableEntity
> doFind(
219 @RequestParam(value
= "query", required
= false) String query
,
220 @RequestParam(value
= "page", required
= false) Integer page
,
221 @RequestParam(value
= "pageSize", required
= false) Integer pageSize
,
222 @RequestParam(value
= "doTaxa", required
= false) Boolean doTaxa
,
223 @RequestParam(value
= "doSynonyms", required
= false) Boolean doSynonyms
,
224 @RequestParam(value
= "doTaxaByCommonNames", required
= false) Boolean doTaxaByCommonNames
,
225 @RequestParam(value
= "area", required
= false) Set
<NamedArea
> areas
,
226 @RequestParam(value
= "treeUuid", required
= false) UUID treeUuid
) throws IOException
{
228 if(page
== null){ page
= BaseListController
.DEFAULT_PAGE
;}
229 if(pageSize
== null){ pageSize
= BaseListController
.DEFAULT_PAGESIZE
;}
231 ITaxonServiceConfigurator config
= new TaxonServiceConfiguratorImpl();
232 config
.setPageNumber(page
);
233 config
.setPageSize(pageSize
);
234 config
.setSearchString(query
);
235 config
.setDoTaxa(doTaxa
!= null ? doTaxa
: Boolean
.FALSE
);
236 config
.setDoSynonyms(doSynonyms
!= null ? doSynonyms
: Boolean
.FALSE
);
237 config
.setDoTaxaByCommonNames(doTaxaByCommonNames
!= null ? doTaxaByCommonNames
: Boolean
.FALSE
);
238 config
.setMatchMode(MatchMode
.BEGINNING
);
239 config
.setTaxonPropertyPath(SIMPLE_TAXON_INIT_STRATEGY
);
240 config
.setNamedAreas(areas
);
241 if(treeUuid
!= null){
242 TaxonomicTree taxonomicTree
= service
.getTaxonomicTreeByUuid(treeUuid
);
243 config
.setTaxonomicTree(taxonomicTree
);
246 return (Pager
<IdentifiableEntity
>) service
.findTaxaAndNames(config
);
251 value
= {"/*/portal/taxon/*/synonymy"},
252 method
= RequestMethod
.GET
)
253 public ModelAndView
doGetSynonymy(HttpServletRequest request
, HttpServletResponse response
)throws IOException
{
254 ModelAndView mv
= new ModelAndView();
255 TaxonBase tb
= getCdmBase(request
, response
, null, Taxon
.class);
256 Taxon taxon
= (Taxon
)tb
;
257 Map
<String
, List
<?
>> synonymy
= new Hashtable
<String
, List
<?
>>();
258 synonymy
.put("homotypicSynonymsByHomotypicGroup", service
.getHomotypicSynonymsByHomotypicGroup(taxon
, SYNONYMY_INIT_STRATEGY
));
259 synonymy
.put("heterotypicSynonymyGroups", service
.getHeterotypicSynonymyGroups(taxon
, SYNONYMY_INIT_STRATEGY
));
260 mv
.addObject(synonymy
);
265 value
= {"/*/portal/taxon/*/taxonRelationships"},
266 method
= RequestMethod
.GET
)
267 public List
<TaxonRelationship
> doGetTaxonRelations(HttpServletRequest request
, HttpServletResponse response
)throws IOException
{
269 TaxonBase tb
= getCdmBase(request
, response
, null, Taxon
.class);
270 Taxon taxon
= (Taxon
)tb
;
271 List
<TaxonRelationship
> relations
= new ArrayList
<TaxonRelationship
>();
272 List
<TaxonRelationship
> results
= service
.listToTaxonRelationships(taxon
, TaxonRelationshipType
.MISAPPLIED_NAME_FOR(), null, null, null, TAXONRELATIONSHIP_INIT_STRATEGY
);
273 relations
.addAll(results
);
274 results
= service
.listToTaxonRelationships(taxon
, TaxonRelationshipType
.INVALID_DESIGNATION_FOR(), null, null, null, TAXONRELATIONSHIP_INIT_STRATEGY
);
275 relations
.addAll(results
);
281 value
= {"/*/portal/taxon/*/nameRelationships"},
282 method
= RequestMethod
.GET
)
283 public List
<NameRelationship
> doGetNameRelations(HttpServletRequest request
, HttpServletResponse response
)throws IOException
{
284 TaxonBase tb
= getCdmBase(request
, response
, SIMPLE_TAXON_INIT_STRATEGY
, Taxon
.class);
285 List
<NameRelationship
> list
= nameService
.listToNameRelationships(tb
.getName(), null, null, null, null, NAMERELATIONSHIP_INIT_STRATEGY
);
290 value
= {"/*/portal/name/*/descriptions"},
291 method
= RequestMethod
.GET
)
292 public List
<TaxonNameDescription
> doGetNameDescriptions(HttpServletRequest request
, HttpServletResponse response
)throws IOException
{
293 UUID nameUuuid
= readValueUuid(request
, null);
294 TaxonNameBase tnb
= nameService
.load(nameUuuid
, null);
295 Pager
<TaxonNameDescription
> p
= descriptionService
.getTaxonNameDescriptions(tnb
, null, null, NAMEDESCRIPTION_INIT_STRATEGY
);
296 return p
.getRecords();
300 value
= {"/*/portal/taxon/*/nameTypeDesignations"},
301 method
= RequestMethod
.GET
)
302 public List
<TypeDesignationBase
> doGetNameTypeDesignations(HttpServletRequest request
, HttpServletResponse response
)throws IOException
{
303 TaxonBase tb
= getCdmBase(request
, response
, SIMPLE_TAXON_INIT_STRATEGY
, Taxon
.class);
304 Pager
<TypeDesignationBase
> p
= nameService
.getTypeDesignations(tb
.getName(), null, null, null, TYPEDESIGNATION_INIT_STRATEGY
);
305 return p
.getRecords();
309 value
= {"/*/portal/taxon/*/descriptions"},
310 method
= RequestMethod
.GET
)
311 public List
<TaxonDescription
> doGetDescriptions(HttpServletRequest request
, HttpServletResponse response
)throws IOException
{
312 Taxon t
= getCdmBase(request
, response
, null, Taxon
.class);
313 Pager
<TaxonDescription
> p
= descriptionService
.getTaxonDescriptions(t
, null, null, null, null, TAXONDESCRIPTION_INIT_STRATEGY
);
314 return p
.getRecords();
318 * Usage /*/portal/name/{taxon
319 * uuid}/media/{mime type
320 * list}/{size}[,[widthOrDuration}][,{height}]/
324 * <li><b>{mime type list}</b>: a comma separated list of mime types, in the
325 * order of preference. The forward slashes contained in the mime types must
326 * be replaced by a colon. Regular expressions can be used. Each media
327 * associated with this given taxon is being searched whereas the first
328 * matching mime type matching a representation always rules.</li>
329 * <li><b>{size},{widthOrDuration},{height}</b>: <i>not jet implemented</i>
330 * valid values are an integer or the asterisk '*' as a wildcard</li>
336 * @throws IOException
339 value
= {"/*/portal/taxon/*/media/*/*"},
340 method
= RequestMethod
.GET
)
341 public List
<Media
> doGetMedia(HttpServletRequest request
, HttpServletResponse response
)throws IOException
{
342 Taxon t
= getCdmBase(request
, response
, null, Taxon
.class);
343 Pager
<TaxonDescription
> p
= descriptionService
.getTaxonDescriptions(t
, null, null, null, null, TAXONDESCRIPTION_INIT_STRATEGY
);
345 // pars the media and quality parameters
348 // collect all media of the given taxon
349 boolean limitToGalleries
= false;
350 List
<Media
> taxonMedia
= new ArrayList
<Media
>();
351 for(TaxonDescription desc
: p
.getRecords()){
352 if(!limitToGalleries
|| desc
.isImageGallery()){
353 for(DescriptionElementBase element
: desc
.getElements()){
354 for(Media media
: element
.getMedia()){
355 taxonMedia
.add(media
);
361 // find best matching representations of each media
362 String path
= request
.getServletPath();
363 String
[] pathTokens
= path
.split("/");
364 String
[] mimeTypes
= pathTokens
[6].split(",");
365 String
[] sizeTokens
= pathTokens
[7].split(",");
366 Integer widthOrDuration
= null;
367 Integer height
= null;
370 for(int i
=0; i
<mimeTypes
.length
; i
++){
371 mimeTypes
[i
] = mimeTypes
[i
].replace(':', '/');
374 if(sizeTokens
.length
> 0){
376 size
= Integer
.valueOf(sizeTokens
[0]);
377 } catch (NumberFormatException nfe
) {
381 if(sizeTokens
.length
> 1){
383 widthOrDuration
= Integer
.valueOf(sizeTokens
[1]);
384 } catch (NumberFormatException nfe
) {
388 if(sizeTokens
.length
> 2){
390 height
= Integer
.valueOf(sizeTokens
[2]);
391 } catch (NumberFormatException nfe
) {
396 List
<Media
> returnMedia
= new ArrayList
<Media
>(taxonMedia
.size());
397 for(Media media
: taxonMedia
){
398 SortedMap
<String
, MediaRepresentation
> prefRepresentations
= orderMediaRepresentations(media
, mimeTypes
, size
, widthOrDuration
, height
);
400 // take first one and remove all other representations
401 MediaRepresentation prefOne
= prefRepresentations
.get(prefRepresentations
.firstKey());
402 for (MediaRepresentation representation
: media
.getRepresentations()) {
403 if (representation
!= prefOne
) {
404 media
.removeRepresentation(representation
);
407 returnMedia
.add(media
);
408 } catch (NoSuchElementException nse
) {
418 * @param mimeTypeRegexes
420 * @param widthOrDuration
424 * TODO move into a media utils class
425 * TODO implement the quality filter
427 private SortedMap
<String
, MediaRepresentation
> orderMediaRepresentations(Media media
, String
[] mimeTypeRegexes
,
428 Integer size
, Integer widthOrDuration
, Integer height
) {
430 SortedMap
<String
, MediaRepresentation
> prefRepr
= new TreeMap
<String
, MediaRepresentation
>();
431 for (String mimeTypeRegex
: mimeTypeRegexes
) {
432 // getRepresentationByMimeType
433 Pattern mimeTypePattern
= Pattern
.compile(mimeTypeRegex
);
434 int representationCnt
= 0;
435 for (MediaRepresentation representation
: media
.getRepresentations()) {
436 Matcher mather
= mimeTypePattern
.matcher(representation
.getMimeType());
437 if (mather
.matches()) {
440 /* TODO the quality filter part is being skipped
441 * // look for representation with the best matching parts
442 for (MediaRepresentationPart part : representation.getParts()) {
443 if (part instanceof ImageFile) {
444 ImageFile image = (ImageFile) part;
445 int dw = image.getWidth() * image.getHeight() - height * widthOrDuration;
451 dwa = (representation.getParts().size() > 0 ? dwa / representation.getParts().size() : 0);
453 prefRepr
.put((dwa
+ representationCnt
++) + '_' + representation
.getMimeType(), representation
);
455 // preferred mime type found => end loop
464 // value = {"/*/portal/taxon/*/descriptions"},
465 // method = RequestMethod.GET)
466 // public List<TaxonDescription> doGetDescriptionsbyFeatureTree(HttpServletRequest request, HttpServletResponse response)throws IOException {
467 // TaxonBase tb = getCdmBase(request, response, null, Taxon.class);
468 // if(tb instanceof Taxon){
469 // //TODO this is a quick and dirty implementation -> generalize
470 // UUID featureTreeUuid = readValueUuid(request, featureTreeUuidPattern);
472 // FeatureTree featureTree = descriptionService.getFeatureTreeByUuid(featureTreeUuid);
473 // Pager<TaxonDescription> p = descriptionService.getTaxonDescriptions((Taxon)tb, null, null, null, null, TAXONDESCRIPTION_INIT_STRATEGY);
474 // List<TaxonDescription> descriptions = p.getRecords();
476 // if(!featureTree.isDescriptionSeparated()){
478 // TaxonDescription superDescription = TaxonDescription.NewInstance();
479 // //put all descriptionElements in superDescription and make it invisible
480 // for(TaxonDescription description: descriptions){
481 // for(DescriptionElementBase element: description.getElements()){
482 // superDescription.addElement(element);
485 // List<TaxonDescription> separatedDescriptions = new ArrayList<TaxonDescription>(descriptions.size());
486 // separatedDescriptions.add(superDescription);
487 // return separatedDescriptions;
489 // return descriptions;
492 // response.sendError(HttpServletResponse.SC_NOT_FOUND, "invalid type; Taxon expected but " + tb.getClass().getSimpleName() + " found.");