ref #10441: some more propertypath adaptions
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / TaxonPortalController.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.remote.controller;
10
11 import java.io.IOException;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.EnumSet;
15 import java.util.HashSet;
16 import java.util.Hashtable;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.UUID;
22 import java.util.stream.Collectors;
23
24 import javax.servlet.http.HttpServletRequest;
25 import javax.servlet.http.HttpServletResponse;
26
27 import org.apache.logging.log4j.LogManager;
28 import org.apache.logging.log4j.Logger;
29 import org.springframework.beans.factory.annotation.Autowired;
30 import org.springframework.stereotype.Controller;
31 import org.springframework.web.bind.WebDataBinder;
32 import org.springframework.web.bind.annotation.InitBinder;
33 import org.springframework.web.bind.annotation.PathVariable;
34 import org.springframework.web.bind.annotation.RequestMapping;
35 import org.springframework.web.bind.annotation.RequestMethod;
36 import org.springframework.web.bind.annotation.RequestParam;
37 import org.springframework.web.bind.annotation.ResponseBody;
38 import org.springframework.web.servlet.ModelAndView;
39
40 import eu.etaxonomy.cdm.api.dto.portal.DistributionInfoDto.InfoPart;
41 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto;
42 import eu.etaxonomy.cdm.api.dto.portal.config.DistributionInfoConfiguration;
43 import eu.etaxonomy.cdm.api.dto.portal.config.DistributionOrder;
44 import eu.etaxonomy.cdm.api.dto.portal.config.TaxonPageDtoConfiguration;
45 import eu.etaxonomy.cdm.api.service.INameService;
46 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
47 import eu.etaxonomy.cdm.api.service.ITaxonService;
48 import eu.etaxonomy.cdm.api.service.ITermService;
49 import eu.etaxonomy.cdm.api.service.portal.IPortalDtoService;
50 import eu.etaxonomy.cdm.api.util.TaxonRelationshipEdge;
51 import eu.etaxonomy.cdm.common.CdmUtils;
52 import eu.etaxonomy.cdm.database.UpdatableRoutingDataSource;
53 import eu.etaxonomy.cdm.format.description.distribution.CondensedDistributionConfiguration;
54 import eu.etaxonomy.cdm.format.description.distribution.CondensedDistributionRecipe;
55 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
56 import eu.etaxonomy.cdm.model.common.CdmBase;
57 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
58 import eu.etaxonomy.cdm.model.common.MarkerType;
59 import eu.etaxonomy.cdm.model.common.RelationshipBase.Direction;
60 import eu.etaxonomy.cdm.model.description.Feature;
61 import eu.etaxonomy.cdm.model.location.NamedArea;
62 import eu.etaxonomy.cdm.model.location.NamedAreaLevel;
63 import eu.etaxonomy.cdm.model.media.Media;
64 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
65 import eu.etaxonomy.cdm.model.name.NameRelationship;
66 import eu.etaxonomy.cdm.model.taxon.Synonym;
67 import eu.etaxonomy.cdm.model.taxon.Taxon;
68 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
69 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
70 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
71 import eu.etaxonomy.cdm.persistence.dao.initializer.EntityInitStrategy;
72 import eu.etaxonomy.cdm.persistence.query.MatchMode;
73 import eu.etaxonomy.cdm.remote.controller.util.ControllerUtils;
74 import eu.etaxonomy.cdm.remote.controller.util.IMediaToolbox;
75 import eu.etaxonomy.cdm.remote.editor.CdmTypePropertyEditor;
76 import eu.etaxonomy.cdm.remote.editor.DefinedTermBaseList;
77 import eu.etaxonomy.cdm.remote.editor.MatchModePropertyEditor;
78 import eu.etaxonomy.cdm.remote.editor.NamedAreaPropertyEditor;
79 import eu.etaxonomy.cdm.remote.editor.TermBaseListPropertyEditor;
80 import eu.etaxonomy.cdm.remote.editor.UUIDListPropertyEditor;
81 import eu.etaxonomy.cdm.remote.editor.UuidList;
82 import io.swagger.annotations.Api;
83
84 /**
85 * The TaxonPortalController class is a Spring MVC Controller.
86 * <p>
87 * The syntax of the mapped service URIs contains the the {datasource-name} path element.
88 * The available {datasource-name}s are defined in a configuration file which
89 * is loaded by the {@link UpdatableRoutingDataSource}. If the
90 * UpdatableRoutingDataSource is not being used in the actual application
91 * context any arbitrary {datasource-name} may be used.
92 * <p>
93 * Methods mapped at type level, inherited from super classes ({@link BaseController}):
94 * <blockquote>
95 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}</b>
96 *
97 * Get the {@link TaxonBase} instance identified by the <code>{taxon-uuid}</code>.
98 * The returned Taxon is initialized by
99 * the following strategy {@link #TAXON_INIT_STRATEGY}
100 * </blockquote>
101 *
102 * @author a.kohlbecker
103 * @since 20.07.2009
104 */
105 @Controller
106 @Api("portal_taxon")
107 @RequestMapping(value = {"/portal/taxon/{uuid}"})
108 public class TaxonPortalController extends TaxonController{
109
110 private static final Logger logger = LogManager.getLogger();
111
112 @Autowired
113 private INameService nameService;
114
115 @Autowired
116 private ITaxonNodeService taxonNodeService;
117
118 @Autowired
119 private ITaxonService taxonService;
120
121 @Autowired
122 private ITermService termService;
123
124 @Autowired
125 private IMediaToolbox mediaToolbox;
126
127 @Autowired
128 private IPortalDtoService portalDtoService;
129
130
131 public static final EntityInitStrategy TAXON_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
132 "$",
133 "sources",
134 "statusNote",
135 "relationsFromThisTaxon.toTaxon.secSource.citation.authorship",
136 "relationsFromThisTaxon.toTaxon.secSource.citation.inReference.authorship",
137 "relationsToThisTaxon.fromTaxon.secSource.citation.authorship",
138 "relationsToThisTaxon.fromTaxon.secSource.citation.inReference.authorship",
139 // the name
140 "name.$",
141 "name.nomenclaturalSource.citation.authorship",
142 "name.nomenclaturalSource.citation.inReference.authorship",
143 "name.rank.representations",
144 "name.status.type.representations",
145 "name.status.source.citation",
146 "secSource.nameUsedInSource.$",
147 "secSource.nameUsedInSource.nomenclaturalSource.citation.authorship",
148 "secSource.nameUsedInSource.nomenclaturalSource.citation.inReference.authorship",
149 "secSource.citation.authorship.$",
150 "secSource.citation.inReference.authorship.$",
151 "annotations.$",
152 "annotations.annotationType.$",
153 "annotations.annotationType.includes.$"
154 // "descriptions" // TODO remove
155
156 }));
157
158 public static final EntityInitStrategy TAXON_WITH_CHILDNODES_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
159 "taxonNodes.$",
160 "taxonNodes.classification.$",
161 "taxonNodes.childNodes.$"
162 }));
163
164 public static final EntityInitStrategy SIMPLE_TAXON_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
165 "$",
166 // the name
167 "name.$",
168 "name.rank.representations",
169 "name.status.type.representations",
170 "name.status.source.citation",
171 "name.nomenclaturalSource.citation.authorship",
172 "name.nomenclaturalSource.citation.inReference.authorship",
173 "taxonNodes.classification",
174 "secSource.nameUsedInSource.$",
175 "secSource.citation.authorship.$",
176 "secSource.citation.inReference.authorship.$"
177 }));
178
179 public static final EntityInitStrategy SYNONYMY_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
180 // initialize homotypical and heterotypical groups; needs synonyms
181 "synonyms.$",
182 "synonyms.name.status.type.representations",
183 "synonyms.name.status.source.citation",
184 "synonyms.name.nomenclaturalSource.citation.authorship",
185 "synonyms.name.nomenclaturalSource.citation.inReference.authorship",
186 // "synonyms.name.homotypicalGroup.typifiedNames.$",
187 // "synonyms.name.homotypicalGroup.typifiedNames.taxonBases.$",
188 "synonyms.name.combinationAuthorship.$",
189 "synonyms.secSource.citation.authorship.$",
190 "synonyms.secSource.citation.inReference.authorship.$",
191 "synonyms.secSource.nameUsedInSource.$",
192 "name.typeDesignations",
193
194 "name.homotypicalGroup.$",
195 "name.homotypicalGroup.typifiedNames.$",
196 "name.homotypicalGroup.typifiedNames.nomenclaturalSource.citation.authorship",
197 "name.homotypicalGroup.typifiedNames.nomenclaturalSource.citation.inReference.authorship",
198 "synonyms.annotations.$",
199 "synonyms.annotations.annotationType.$",
200 "synonyms.annotations.annotationType.includes.$"
201
202 // "name.homotypicalGroup.typifiedNames.taxonBases.$"
203 }));
204
205
206 private static final EntityInitStrategy TAXONRELATIONSHIP_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
207 "$",
208 "type.inverseRepresentations",
209 "fromTaxon.sec",
210 "fromTaxon.name",
211 "toTaxon.sec",
212 "toTaxon.name"
213 }));
214
215 public static final EntityInitStrategy NAMERELATIONSHIP_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
216 "$",
217 "type.inverseRepresentations",
218 "source.citation",
219 "toName.$",
220 "toName.nomenclaturalSource.citation.authorship",
221 "toName.nomenclaturalSource.citation.inReference.authorship",
222 "fromName.$",
223 "fromName.nomenclaturalSource.citation.authorship",
224 "fromName.nomenclaturalSource.citation.inReference.authorship",
225
226 }));
227
228 protected static final EntityInitStrategy TAXONDESCRIPTION_INIT_STRATEGY = DescriptionPortalController.DESCRIPTION_INIT_STRATEGY;
229
230 protected static final EntityInitStrategy DESCRIPTION_ELEMENT_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
231 "$",
232 "sources.citation.authorship",
233 "sources.citation.inReference.authorship",
234 "sources.nameUsedInSource",
235 "multilanguageText",
236 "media",
237 }));
238
239
240 // private static final List<String> NAMEDESCRIPTION_INIT_STRATEGY = Arrays.asList(new String []{
241 // "uuid",
242 // "feature",
243 // "elements.$",
244 // "elements.multilanguageText",
245 // "elements.media",
246 // });
247
248 protected static final EntityInitStrategy TAXONDESCRIPTION_MEDIA_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
249 "elements.media"
250
251 }));
252
253 private static final EntityInitStrategy TYPEDESIGNATION_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
254 "typeSpecimen.$",
255 "citation.authorship.$",
256 "typeName",
257 "typeStatus"
258 }));
259
260 protected static final EntityInitStrategy TAXONNODE_WITH_CHILDNODES_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
261 "childNodes.taxon",
262 }));
263
264 protected static final EntityInitStrategy TAXONNODE_INIT_STRATEGY = new EntityInitStrategy(Arrays.asList(new String []{
265 "taxonNodes.classification",
266 "taxonNodes.parent",
267 "taxonNodes.statusNote.*",
268 "taxonNodes.taxon.name",
269 "taxonNodes.taxon.secSource.citation",
270 "taxonNodes.taxon.secSource.nameUsedInSource.$",
271 "taxonNodes.taxon.secSource.citation.authorship.$",
272 "taxonNodes.taxon.secSource.citation.inReference.authorship.$",
273 "taxonNodes.source.citation.authorship",
274 "taxonNodes.source.citation.inReference.authorship",
275 "acceptedTaxon.taxonNodes.classification",
276 "secSource.nameUsedInSource"
277
278 }));
279
280 @Override
281 protected <CDM_BASE extends CdmBase> List<String> complementInitStrategy(Class<CDM_BASE> clazz,
282 List<String> pathProperties) {
283
284 if(pathProperties != null) {
285 List<String> complemented = new ArrayList<>(pathProperties);
286 if(pathProperties.contains("name")) {
287 // pathProperties for web service request for portal/taxon/{uuid}/name
288 complemented.add("name.nomenclaturalSource.citation.authorship");
289 complemented.add("name.nomenclaturalSource.citation.inReference.authorship");
290 return complemented;
291 }
292 }
293 return pathProperties;
294 }
295
296 public TaxonPortalController(){
297 super();
298 setInitializationStrategy(TAXON_INIT_STRATEGY.getPropertyPaths());
299 }
300
301 @Autowired
302 @Override
303 public void setService(ITaxonService service) {
304 this.service = service;
305 }
306
307 @InitBinder
308 @Override
309 public void initBinder(WebDataBinder binder) {
310 super.initBinder(binder);
311 binder.registerCustomEditor(NamedArea.class, new NamedAreaPropertyEditor());
312 binder.registerCustomEditor(MatchMode.class, new MatchModePropertyEditor());
313 binder.registerCustomEditor(Class.class, new CdmTypePropertyEditor());
314 binder.registerCustomEditor(UuidList.class, new UUIDListPropertyEditor());
315 binder.registerCustomEditor(DefinedTermBaseList.class, new TermBaseListPropertyEditor<>(termService));
316 }
317
318 /**
319 * TODO documentation
320 *
321 * @param taxonUuid the taxon uuid
322 * @param subtreeUuid the taxon node subtree filter
323 * @throws IOException
324 */
325 @RequestMapping(
326 value = {"page"},
327 method = RequestMethod.GET)
328 @ResponseBody
329 public TaxonPageDto doGetTaxonPage(@PathVariable("uuid") UUID taxonUuid,
330 @RequestParam(value = "subtree", required = false) UUID subtreeUuid,
331 @RequestParam(value = "featureTree", required = false) UUID featureTreeUuid,
332 @RequestParam(value = "nameRelationsDirect", required = false) Set<UUID> directNameRelations,
333 @RequestParam(value = "nameRelationsInverse", required = false) Set<UUID> inverseNameRelations,
334 @RequestParam(value = "etAlPos", required = false) Integer etAlPosition,
335 @RequestParam(value = "doSynonyms", required = false) boolean doSynonyms,
336 @RequestParam(value = "doFacts", required = false) boolean doFacts,
337 @RequestParam(value = "doSpecimens", required = false) boolean doSpecimens,
338 @RequestParam(value = "doKeys", required = false) boolean doKeys,
339 @RequestParam(value = "doMedia", required = false) boolean doMedia,
340 @RequestParam(value = "doTaxonNodes", required = false) boolean doTaxonNodes,
341 @RequestParam(value = "doTaxonRelations", required = false) boolean doTaxonRelations,
342 //TODO annotation type filter
343
344 //distributionInfoConfig
345 @RequestParam(value = "part", required = false) Set<InfoPart> partSet,
346 @RequestParam(value = "subAreaPreference", required = false) boolean preferSubAreas,
347 @RequestParam(value = "statusOrderPreference", required = false) boolean statusOrderPreference,
348 @RequestParam(value = "fallbackAreaMarkerType", required = false) DefinedTermBaseList<MarkerType> fallbackAreaMarkerTypeList,
349 @RequestParam(value = "alternativeRootAreaMarkerType", required = false) DefinedTermBaseList<MarkerType> alternativeRootAreaMarkerTypeList,
350 @RequestParam(value = "areaTree", required = false ) UUID areaTreeUuid,
351 //TODO still needs to be used
352 @RequestParam(value = "statusTree", required = false ) UUID statusTreeUuid,
353 @RequestParam(value = "omitLevels", required = false) Set<NamedAreaLevel> omitLevels,
354 @RequestParam(value = "statusColors", required = false) String statusColorsString,
355 @RequestParam(value = "distributionOrder", required = false, defaultValue="LABEL") DistributionOrder distributionOrder,
356 @RequestParam(value = "recipe", required = false, defaultValue="EuroPlusMed") CondensedDistributionRecipe recipe,
357
358 //TODO configuration data
359 HttpServletRequest request,
360 HttpServletResponse response) throws IOException {
361
362 boolean includeUnpublished = NO_UNPUBLISHED;
363 if(request != null){
364 logger.info("doGetTaxonPage() " + requestPathAndQuery(request));
365 }
366
367 //TODO for now hardcoded
368 alternativeRootAreaMarkerTypeList = new DefinedTermBaseList<>();
369 UUID alternativeRootAreaMarkerTypeUuid = MarkerType.uuidAlternativeRootArea;
370 MarkerType defaultAlternativeRootAreaMarkerType = (MarkerType)termService.find(alternativeRootAreaMarkerTypeUuid);
371 if (defaultAlternativeRootAreaMarkerType != null) {
372 alternativeRootAreaMarkerTypeList.add(defaultAlternativeRootAreaMarkerType);
373 }
374
375
376 //TODO is this current state of art?
377 // ModelAndView mv = new ModelAndView();
378
379 //check taxon exists and not filtered
380 Taxon taxon = getCdmBaseInstance(Taxon.class, taxonUuid, response, getTaxonNodeInitStrategy().getPropertyPaths());
381 TaxonNode subtree = getSubtreeOrError(subtreeUuid, taxonNodeService, response);
382 taxon = checkExistsSubtreeAndAccess(taxon, subtree, NO_UNPUBLISHED, response);
383
384 if (partSet == null) {
385 partSet = EnumSet.of(InfoPart.condensedDistribution, InfoPart.mapUriParams, InfoPart.tree);
386 }
387 partSet.remove(InfoPart.elements); // we do not want to return model instances here at all
388
389 // //TODO is this performant?
390 // IVocabularyService vocabularyService = null;
391 // Map<PresenceAbsenceTerm, Color> distributionStatusColors = DistributionServiceUtilities.buildStatusColorMap(
392 // statusColorsString, termService, vocabularyService);
393
394 TaxonPageDtoConfiguration config = new TaxonPageDtoConfiguration();
395
396 config.setTaxonUuid(taxonUuid);
397 config.setFeatureTree(featureTreeUuid);
398 config.setEtAlPosition(etAlPosition);
399 config.setWithFacts(doFacts);
400 config.setWithKeys(doKeys);
401 config.setWithMedia(doMedia);
402 config.setWithSpecimens(doSpecimens);
403 config.setWithSynonyms(doSynonyms);
404 config.setWithTaxonNodes(doTaxonNodes);
405 config.setWithTaxonRelationships(doTaxonRelations);
406 config.setIncludeUnpublished(includeUnpublished);
407
408 Set<MarkerType> fallbackAreaMarkerTypes = new HashSet<>();
409 if(!CdmUtils.isNullSafeEmpty(fallbackAreaMarkerTypeList)){
410 fallbackAreaMarkerTypes = fallbackAreaMarkerTypeList.asSet();
411 }
412 Set<MarkerType> alternativeRootAreaMarkerTypes = new HashSet<>();
413 if(!CdmUtils.isNullSafeEmpty(alternativeRootAreaMarkerTypeList)){
414 alternativeRootAreaMarkerTypes = alternativeRootAreaMarkerTypeList.asSet();
415 }
416
417 //default distribution info config
418 DistributionInfoConfiguration distributionConfig = config.getDistributionInfoConfiguration();
419 distributionConfig.setIncludeUnpublished(includeUnpublished);
420 distributionConfig.setUseTreeDto(true);
421 distributionConfig.setInfoParts(EnumSet.copyOf(partSet));
422 distributionConfig.setPreferSubAreas(preferSubAreas);
423 distributionConfig.setStatusOrderPreference(statusOrderPreference);
424 distributionConfig.setAreaTree(areaTreeUuid);
425 distributionConfig.setStatusTree(statusTreeUuid);
426 distributionConfig.setOmitLevels(omitLevels);
427 distributionConfig.setStatusColorsString(statusColorsString);
428 distributionConfig.setDistributionOrder(distributionOrder);
429 if (recipe != null) {
430 CondensedDistributionConfiguration condensedConfig = recipe.toConfiguration();
431 condensedConfig.alternativeRootAreaMarkers = getUuids(alternativeRootAreaMarkerTypes);
432 distributionConfig.setCondensedDistributionConfiguration(condensedConfig);
433 }
434 distributionConfig.setFallbackAreaMarkerTypeList(fallbackAreaMarkerTypes); //was (remove if current implementation works): fallbackAreaMarkerTypes.stream().map(mt->mt.getUuid()).collect(Collectors.toSet());
435 distributionConfig.setAlternativeRootAreaMarkerTypes(alternativeRootAreaMarkerTypes);
436
437 //iucn distribution info config
438 DistributionInfoConfiguration iucnDistributionConfig = new DistributionInfoConfiguration();
439 iucnDistributionConfig.setIncludeUnpublished(includeUnpublished);
440 config.putDistributionInfoConfiguration(Feature.uuidIucnStatus, iucnDistributionConfig);
441 iucnDistributionConfig.setUseTreeDto(true);
442 EnumSet<InfoPart> iucnPartSet = EnumSet.of(InfoPart.condensedDistribution);
443 iucnDistributionConfig.setInfoParts(iucnPartSet);
444
445 iucnDistributionConfig.setPreferSubAreas(preferSubAreas);
446 iucnDistributionConfig.setStatusOrderPreference(statusOrderPreference);
447 iucnDistributionConfig.setAreaTree(areaTreeUuid);
448 //TODO IUCN status tree?
449 iucnDistributionConfig.setOmitLevels(omitLevels);
450 // distributionConfig.setStatusColorsString(statusColorsString);
451 iucnDistributionConfig.setDistributionOrder(distributionOrder);
452 CondensedDistributionRecipe iucnRecipe = CondensedDistributionRecipe.IUCN;
453 if (iucnRecipe != null) {
454 CondensedDistributionConfiguration condensedConfig = iucnRecipe.toConfiguration();
455 condensedConfig.alternativeRootAreaMarkers = getUuids(alternativeRootAreaMarkerTypes);
456 iucnDistributionConfig.setCondensedDistributionConfiguration(condensedConfig);
457 }
458 iucnDistributionConfig.setFallbackAreaMarkerTypeList(fallbackAreaMarkerTypes);
459 iucnDistributionConfig.setAlternativeRootAreaMarkerTypes(alternativeRootAreaMarkerTypes);
460
461 TaxonPageDto dto = portalDtoService.taxonPageDto(config);
462 return dto;
463 }
464
465 private Set<UUID> getUuids(Set<? extends CdmBase> entities) {
466 return entities.stream().map(e->e.getUuid()).collect(Collectors.toSet());
467 }
468
469 /**
470 * Get the synonymy for a taxon identified by the <code>{taxon-uuid}</code>.
471 * The synonymy consists
472 * of two parts: The group of homotypic synonyms of the taxon and the
473 * heterotypic synonymy groups of the taxon. The synonymy is ordered
474 * historically by the type designations and by the publication date of the
475 * nomenclatural reference
476 * <p>
477 * URI:
478 * <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;synonymy</b>
479 *
480 *
481 * @param request
482 * @param response
483 * @return a Map with two entries which are mapped by the following keys:
484 * "homotypicSynonymsByHomotypicGroup", "heterotypicSynonymyGroups",
485 * containing lists of {@link Synonym}s which // TODO Auto-generated catch block
486 e.printStackTrace();are initialized using the
487 * following initialization strategy: {@link #SYNONYMY_INIT_STRATEGY}
488 *
489 * @throws IOException
490 */
491 @RequestMapping(
492 value = {"synonymy"},
493 method = RequestMethod.GET)
494 public ModelAndView doGetSynonymy(@PathVariable("uuid") UUID taxonUuid,
495 @RequestParam(value = "subtree", required = false) UUID subtreeUuid,
496 HttpServletRequest request,
497 HttpServletResponse response)throws IOException {
498
499 boolean includeUnpublished = NO_UNPUBLISHED;
500 if(request != null){
501 logger.info("doGetSynonymy() " + requestPathAndQuery(request));
502 }
503 ModelAndView mv = new ModelAndView();
504
505 Taxon taxon = getCdmBaseInstance(Taxon.class, taxonUuid, response, getTaxonNodeInitStrategy().getPropertyPaths());
506 TaxonNode subtree = getSubtreeOrError(subtreeUuid, taxonNodeService, response);
507 taxon = checkExistsSubtreeAndAccess(taxon, subtree, NO_UNPUBLISHED, response);
508
509 Map<String, List<?>> synonymy = new Hashtable<>();
510
511 //new
512 List<List<Synonym>> synonymyGroups = service.getSynonymsByHomotypicGroup(taxon, SYNONYMY_INIT_STRATEGY.getPropertyPaths());
513 if(!includeUnpublished){
514 synonymyGroups = removeUnpublishedSynonyms(synonymyGroups);
515 }
516
517 synonymy.put("homotypicSynonymsByHomotypicGroup", synonymyGroups.get(0));
518 synonymyGroups.remove(0);
519 synonymy.put("heterotypicSynonymyGroups", synonymyGroups);
520
521 //old
522 // synonymy.put("homotypicSynonymsByHomotypicGroup", service.getHomotypicSynonymsByHomotypicGroup(taxon, SYNONYMY_INIT_STRATEGY));
523 // synonymy.put("heterotypicSynonymyGroups", service.getHeterotypicSynonymyGroups(taxon, SYNONYMY_INIT_STRATEGY));
524
525 mv.addObject(synonymy);
526 return mv;
527 }
528
529
530 /**
531 * @param synonymyGroups
532 */
533 private List<List<Synonym>> removeUnpublishedSynonyms(List<List<Synonym>> synonymyGroups) {
534 List<List<Synonym>> result = new ArrayList<>();
535 boolean isHomotypicToAccepted = true;
536
537 for (List<Synonym> oldList : synonymyGroups){
538 List<Synonym> newList = new ArrayList<>();
539 for (Synonym oldSyn : oldList){
540 if (oldSyn.isPublish()){
541 newList.add(oldSyn);
542 }
543 }
544 if (isHomotypicToAccepted || !newList.isEmpty()){
545 result.add(newList);
546 }
547 isHomotypicToAccepted = false;
548 }
549 return result;
550 }
551
552 /**
553 * {@inheritDoc}
554 */
555 @Override
556 protected List<String> getTaxonDescriptionInitStrategy() {
557 return TAXONDESCRIPTION_INIT_STRATEGY.getPropertyPaths();
558 }
559
560 @Override
561 protected List<String> getTaxonDescriptionElementInitStrategy() {
562 return DESCRIPTION_ELEMENT_INIT_STRATEGY.getPropertyPaths();
563 }
564
565 @Override
566 protected EntityInitStrategy getTaxonNodeInitStrategy() {
567 return TAXONNODE_INIT_STRATEGY;
568 }
569
570 /**
571 * Get the list of {@link TaxonRelationship}s for the given
572 * {@link TaxonBase} instance identified by the <code>{taxon-uuid}</code>.
573 * <p>
574 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;taxonRelationships</b>
575 *
576 * @param request
577 * @param response
578 * @return a List of {@link TaxonRelationship} entities which are initialized
579 * using the following initialization strategy:
580 * {@link #TAXONRELATIONSHIP_INIT_STRATEGY}
581 * @throws IOException
582 */
583 @RequestMapping(
584 value = {"taxonRelationships"},
585 method = RequestMethod.GET)
586 public List<TaxonRelationship> doGetTaxonRelations(@PathVariable("uuid") UUID uuid,
587 HttpServletRequest request, HttpServletResponse response)throws IOException {
588
589 boolean includeUnpublished = NO_UNPUBLISHED;
590 logger.info("doGetTaxonRelations()" + requestPathAndQuery(request));
591 Taxon taxon = getCdmBaseInstance(Taxon.class, uuid, response, (List<String>)null);
592 taxon = checkExistsAndAccess(taxon, includeUnpublished, response);
593
594 List<TaxonRelationship> toRelationships = service.listToTaxonRelationships(taxon, null,
595 includeUnpublished, null, null, null, TAXONRELATIONSHIP_INIT_STRATEGY.getPropertyPaths());
596 List<TaxonRelationship> fromRelationships = service.listFromTaxonRelationships(taxon, null,
597 includeUnpublished, null, null, null, TAXONRELATIONSHIP_INIT_STRATEGY.getPropertyPaths());
598
599 List<TaxonRelationship> allRelationships = new ArrayList<>(toRelationships.size() + fromRelationships.size());
600 allRelationships.addAll(toRelationships);
601 allRelationships.addAll(fromRelationships);
602
603 return allRelationships;
604 }
605
606 /**
607 * Get the list of {@link NameRelationship}s of the Name associated with the
608 * {@link TaxonBase} instance identified by the <code>{taxon-uuid}</code>.
609 * <p>
610 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;nameRelationships</b>
611 *
612 * @param request
613 * @param response
614 * @return a List of {@link NameRelationship} entities which are initialized
615 * using the following initialization strategy:
616 * {@link #NAMERELATIONSHIP_INIT_STRATEGY}
617 * @throws IOException
618 */
619 @RequestMapping(
620 value = {"toNameRelationships"},
621 method = RequestMethod.GET)
622 public List<NameRelationship> doGetToNameRelations(@PathVariable("uuid") UUID uuid,
623 HttpServletRequest request, HttpServletResponse response)throws IOException {
624 logger.info("doGetNameRelations()" + request.getRequestURI());
625 boolean includeUnpublished = NO_UNPUBLISHED;
626
627 TaxonBase<?> taxonBase = getCdmBaseInstance(TaxonBase.class, uuid, response, (List<String>)null);
628 taxonBase = checkExistsAndAccess(taxonBase, includeUnpublished, response);
629
630 List<NameRelationship> list = nameService.listNameRelationships(taxonBase.getName(), Direction.relatedTo, null, null, 0, null, NAMERELATIONSHIP_INIT_STRATEGY.getPropertyPaths());
631 return list;
632 }
633
634 /**
635 * Get the list of {@link NameRelationship}s of the Name associated with the
636 * {@link TaxonBase} instance identified by the <code>{taxon-uuid}</code>.
637 * <p>
638 * URI: <b>&#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-uuid}&#x002F;nameRelationships</b>
639 *
640 * @param request
641 * @param response
642 * @return a List of {@link NameRelationship} entities which are initialized
643 * using the following initialization strategy:
644 * {@link #NAMERELATIONSHIP_INIT_STRATEGY}
645 * @throws IOException
646 */
647 @RequestMapping(
648 value = {"fromNameRelationships"},
649 method = RequestMethod.GET)
650 public List<NameRelationship> doGetFromNameRelations(
651 @PathVariable("uuid") UUID uuid,
652 HttpServletRequest request,
653 HttpServletResponse response)throws IOException {
654 logger.info("doGetNameFromNameRelations()" + requestPathAndQuery(request));
655
656 boolean includeUnpublished = NO_UNPUBLISHED;
657
658 TaxonBase<?> taxonBase = getCdmBaseInstance(TaxonBase.class, uuid, response, SIMPLE_TAXON_INIT_STRATEGY.getPropertyPaths());
659 taxonBase = checkExistsAndAccess(taxonBase, includeUnpublished, response);
660
661 List<NameRelationship> list = nameService.listNameRelationships(taxonBase.getName(), Direction.relatedFrom, null, null, 0, null, NAMERELATIONSHIP_INIT_STRATEGY.getPropertyPaths());
662
663 return list;
664 }
665
666
667 // @RequestMapping(value = "specimens", method = RequestMethod.GET)
668 // public ModelAndView doGetSpecimens(
669 // @PathVariable("uuid") UUID uuid,
670 // HttpServletRequest request,
671 // HttpServletResponse response) throws IOException, ClassNotFoundException {
672 // logger.info("doGetSpecimens() - " + request.getRequestURI());
673 //
674 // ModelAndView mv = new ModelAndView();
675 //
676 // List<DerivedUnitFacade> derivedUnitFacadeList = new ArrayList<>();
677 //
678 // // find speciemens in the TaxonDescriptions
679 // List<TaxonDescription> taxonDescriptions = doGetDescriptions(uuid, request, response);
680 // if (taxonDescriptions != null) {
681 //
682 // for (TaxonDescription description : taxonDescriptions) {
683 // derivedUnitFacadeList.addAll( occurrenceService.listDerivedUnitFacades(description, null) );
684 // }
685 // }
686 // // TODO find specimens in the NameDescriptions ??
687 //
688 // // TODO also find type specimens
689 //
690 // mv.addObject(derivedUnitFacadeList);
691 //
692 // return mv;
693 // }
694
695 /**
696 * Get the {@link Media} attached to the {@link Taxon} instance
697 * identified by the <code>{taxon-uuid}</code>.
698 *
699 * Usage &#x002F;{datasource-name}&#x002F;portal&#x002F;taxon&#x002F;{taxon-
700 * uuid}&#x002F;media&#x002F;{mime type
701 * list}&#x002F;{size}[,[widthOrDuration}][,{height}]&#x002F;
702 *
703 * Whereas
704 * <ul>
705 * <li><b>{mime type list}</b>: a comma separated list of mime types, in the
706 * order of preference. The forward slashes contained in the mime types must
707 * be replaced by a colon. Regular expressions can be used. Each media
708 * associated with this given taxon is being searched whereas the first
709 * matching mime type matching a representation always rules.</li>
710 * <li><b>{size},{widthOrDuration},{height}</b>: <i>not jet implemented</i>
711 * valid values are an integer or the asterisk '*' as a wildcard</li>
712 * </ul>
713 *
714 * @param request
715 * @param response
716 * @return a List of {@link Media} entities which are initialized
717 * using the following initialization strategy:
718 * {@link #TAXONDESCRIPTION_INIT_STRATEGY}
719 * @throws IOException
720 */
721 // @RequestMapping(
722 // value = {"media"},
723 // method = RequestMethod.GET)
724 // public List<Media> doGetMedia(
725 // @PathVariable("uuid") UUID uuid,
726 // @RequestParam(value = "type", required = false) Class<? extends MediaRepresentationPart> type,
727 // @RequestParam(value = "mimeTypes", required = false) String[] mimeTypes,
728 // @RequestParam(value = "relationships", required = false) UuidList relationshipUuids,
729 // @RequestParam(value = "relationshipsInvers", required = false) UuidList relationshipInversUuids,
730 // @RequestParam(value = "includeTaxonDescriptions", required = true) Boolean includeTaxonDescriptions,
731 // @RequestParam(value = "includeOccurrences", required = true) Boolean includeOccurrences,
732 // @RequestParam(value = "includeTaxonNameDescriptions", required = true) Boolean includeTaxonNameDescriptions,
733 // @RequestParam(value = "widthOrDuration", required = false) Integer widthOrDuration,
734 // @RequestParam(value = "height", required = false) Integer height,
735 // @RequestParam(value = "size", required = false) Integer size,
736 // HttpServletRequest request, HttpServletResponse response) throws IOException {
737 //
738 // logger.info("doGetMedia() " + requestPathAndQuery(request));
739 //
740 // List<String> initStrategy = null;
741 //
742 // EntityMediaContext<Taxon> taxonMediaContext = loadMediaForTaxonAndRelated(uuid, relationshipUuids,
743 // relationshipInversUuids, includeTaxonDescriptions, includeOccurrences, includeTaxonNameDescriptions,
744 // response, initStrategy, MediaPortalController.MEDIA_INIT_STRATEGY.getPropertyPaths());
745 //
746 // List<Media> mediafilteredForPreferredRepresentations = mediaToolbox.processAndFilterPreferredMediaRepresentations(type, mimeTypes, widthOrDuration, height, size,
747 // taxonMediaContext.media);
748 //
749 // return mediafilteredForPreferredRepresentations;
750 // }
751
752 @RequestMapping(
753 value = {"media"},
754 method = RequestMethod.GET)
755 public List<Media> doGetMedia(
756 @PathVariable("uuid") UUID uuid,
757 @RequestParam(value = "type", required = false) Class<? extends MediaRepresentationPart> type,
758 @RequestParam(value = "mimeTypes", required = false) String[] mimeTypes,
759 @RequestParam(value = "relationships", required = false) UuidList relationshipUuids,
760 @RequestParam(value = "relationshipsInvers", required = false) UuidList relationshipInversUuids,
761 @RequestParam(value = "includeTaxonDescriptions", required = true) Boolean includeTaxonDescriptions,
762 @RequestParam(value = "includeOccurrences", required = true) Boolean includeOccurrences,
763 @RequestParam(value = "includeOriginals", required = false) Boolean includeOriginals,
764 @RequestParam(value = "includeTaxonNameDescriptions", required = true) Boolean includeTaxonNameDescriptions,
765 @RequestParam(value = "widthOrDuration", required = false) Integer widthOrDuration,
766 @RequestParam(value = "height", required = false) Integer height,
767 @RequestParam(value = "size", required = false) Integer size,
768 HttpServletRequest request, HttpServletResponse response) throws IOException {
769
770 logger.info("doGetMedia() " + requestPathAndQuery(request));
771
772 List<String> initStrategy = null;
773
774 EntityMediaContext<Taxon> taxonMediaContext = loadMediaForTaxonAndRelated(uuid, relationshipUuids,
775 relationshipInversUuids, includeTaxonDescriptions, includeOccurrences, includeTaxonNameDescriptions,
776 response, initStrategy, MediaPortalController.MEDIA_INIT_STRATEGY.getPropertyPaths());
777
778 List<Media> mediafilteredForPreferredRepresentations = mediaToolbox.processAndFilterPreferredMediaRepresentations(
779 type, mimeTypes, widthOrDuration, height, size, taxonMediaContext.media);
780
781 return mediafilteredForPreferredRepresentations;
782 }
783
784 /**
785 * @Deprecated To be replaced by other loadMediaForTaxonAndRelated method
786 */
787 @Deprecated
788 public EntityMediaContext<Taxon> loadMediaForTaxonAndRelated(UUID uuid,
789 UuidList relationshipUuids, UuidList relationshipInversUuids,
790 Boolean includeTaxonDescriptions, Boolean includeOccurrences, Boolean includeTaxonNameDescriptions,
791 HttpServletResponse response,
792 List<String> taxonInitStrategy) throws IOException {
793 return loadMediaForTaxonAndRelated(uuid, relationshipUuids, relationshipInversUuids,
794 includeTaxonDescriptions, includeOccurrences, includeTaxonNameDescriptions, response, taxonInitStrategy, null);
795 }
796
797 public EntityMediaContext<Taxon> loadMediaForTaxonAndRelated(UUID taxonUuid,
798 UuidList relationshipUuids, UuidList relationshipInversUuids,
799 Boolean includeTaxonDescriptions, Boolean includeOccurrences, Boolean includeTaxonNameDescriptions,
800 HttpServletResponse response,
801 List<String> taxonInitStrategy, List<String> mediaInitStrategy) throws IOException {
802
803 return loadMediaForTaxonAndRelated(taxonUuid,
804 relationshipUuids, relationshipInversUuids,
805 includeTaxonDescriptions, includeOccurrences, false, includeTaxonNameDescriptions,
806 response, taxonInitStrategy, mediaInitStrategy);
807 }
808
809 public EntityMediaContext<Taxon> loadMediaForTaxonAndRelated(UUID taxonUuid,
810 UuidList relationshipUuids, UuidList relationshipInversUuids,
811 Boolean includeTaxonDescriptions, Boolean includeOccurrences, Boolean includeOriginals, Boolean includeTaxonNameDescriptions,
812 HttpServletResponse response,
813 List<String> taxonInitStrategy, List<String> mediaInitStrategy) throws IOException {
814
815 boolean includeUnpublished = NO_UNPUBLISHED;
816
817 Taxon taxon = getCdmBaseInstance(Taxon.class, taxonUuid, response, taxonInitStrategy);
818 taxon = checkExistsAndAccess(taxon, includeUnpublished, response);
819
820 Set<TaxonRelationshipEdge> includeRelationships = ControllerUtils.loadIncludeRelationships(relationshipUuids, relationshipInversUuids, termService);
821
822 List<Media> media = listMediaForTaxon(taxon, includeRelationships,
823 includeTaxonDescriptions, includeOccurrences, includeOriginals, includeTaxonNameDescriptions, includeUnpublished, mediaInitStrategy);
824
825 EntityMediaContext<Taxon> entityMediaContext = new EntityMediaContext<>(taxon, media);
826
827 return entityMediaContext;
828 }
829
830 @RequestMapping(
831 value = {"subtree/media"},
832 method = RequestMethod.GET)
833 public List<Media> doGetSubtreeMedia(
834 @PathVariable("uuid") UUID uuid,
835 @RequestParam(value = "type", required = false) Class<? extends MediaRepresentationPart> type,
836 @RequestParam(value = "mimeTypes", required = false) String[] mimeTypes,
837 @RequestParam(value = "relationships", required = false) UuidList relationshipUuids,
838 @RequestParam(value = "relationshipsInvers", required = false) UuidList relationshipInversUuids,
839 @RequestParam(value = "includeTaxonDescriptions", required = true) Boolean includeTaxonDescriptions,
840 @RequestParam(value = "includeOccurrences", required = true) Boolean includeOccurrences,
841 @RequestParam(value = "includeTaxonNameDescriptions", required = true) Boolean includeTaxonNameDescriptions,
842 @RequestParam(value = "widthOrDuration", required = false) Integer widthOrDuration,
843 @RequestParam(value = "height", required = false) Integer height,
844 @RequestParam(value = "size", required = false) Integer size,
845 HttpServletRequest request, HttpServletResponse response)throws IOException {
846
847 boolean includeUnpublished = NO_UNPUBLISHED;
848
849 logger.info("doGetSubtreeMedia() " + requestPathAndQuery(request));
850
851 List<String> initStrategy = TAXON_WITH_CHILDNODES_INIT_STRATEGY.getPropertyPaths();
852
853 Taxon taxon = getCdmBaseInstance(Taxon.class, uuid, response, initStrategy);
854 taxon = checkExistsAndAccess(taxon, includeUnpublished, response);
855
856 Set<TaxonRelationshipEdge> includeRelationships = ControllerUtils.loadIncludeRelationships(relationshipUuids, relationshipInversUuids, termService);
857
858 boolean includeOriginals = false; //or when unifying methods, do we want this as webservice parameter, too?
859 List<Media> media = listMediaForTaxon(taxon, includeRelationships,
860 includeTaxonDescriptions, includeOccurrences, includeOriginals, includeTaxonNameDescriptions, includeUnpublished, null);
861 media = addTaxonomicChildrenMedia(includeTaxonDescriptions, includeOccurrences, includeOriginals,
862 includeTaxonNameDescriptions, taxon,
863 includeRelationships, media, includeUnpublished);
864
865 List<Media> mediafilteredForPreferredRepresentations = mediaToolbox.processAndFilterPreferredMediaRepresentations(type, mimeTypes, widthOrDuration, height, size,
866 media);
867
868 return mediafilteredForPreferredRepresentations;
869 }
870
871 public List<Media> addTaxonomicChildrenMedia(Boolean includeTaxonDescriptions, Boolean includeOccurrences,
872 boolean includeOriginals, Boolean includeTaxonNameDescriptions, Taxon taxon,
873 Set<TaxonRelationshipEdge> includeRelationships, List<Media> media, boolean includeUnpublished) {
874
875 //TODO use treeindex
876 //looking for all medias of direct children
877 TaxonNode node;
878 if (taxon.getTaxonNodes().size()>0){
879 Set<TaxonNode> nodes = taxon.getTaxonNodes();
880 Iterator<TaxonNode> iterator = nodes.iterator();
881 //TaxonNode holen
882 node = iterator.next();
883 //Check if TaxonNode belongs to the current tree
884
885 node = taxonNodeService.load(node.getUuid(), TAXONNODE_WITH_CHILDNODES_INIT_STRATEGY.getPropertyPaths());
886 List<TaxonNode> children = node.getChildNodes();
887 Taxon childTaxon;
888 for (TaxonNode child : children){
889 childTaxon = child.getTaxon();
890 if(childTaxon != null) {
891 childTaxon = (Taxon)taxonService.load(childTaxon.getUuid(), NO_UNPUBLISHED, null);
892 media.addAll(listMediaForTaxon(childTaxon, includeRelationships,
893 includeTaxonDescriptions, includeOccurrences, includeOriginals,
894 includeTaxonNameDescriptions, includeUnpublished, MediaPortalController.MEDIA_INIT_STRATEGY.getPropertyPaths()));
895 }
896 }
897 }
898 return media;
899 }
900
901 private List<Media> listMediaForTaxon(Taxon taxon, Set<TaxonRelationshipEdge> includeRelationships,
902 Boolean includeTaxonDescriptions, Boolean includeOccurrences, Boolean includeOriginals,
903 Boolean includeTaxonNameDescriptions, boolean includeUnpublished, List<String> propertyPath) {
904
905 List<Media> media = service.listMedia(taxon, includeRelationships,
906 false, includeTaxonDescriptions, includeOccurrences, includeOriginals,
907 includeTaxonNameDescriptions, includeUnpublished, propertyPath);
908
909 return media;
910 }
911
912 public class EntityMediaContext<T extends IdentifiableEntity> {
913
914 private T entity;
915 private List<Media> media;
916
917 public EntityMediaContext(T entity, List<Media> media) {
918 this.entity = HibernateProxyHelper.deproxy(entity);
919 this.media = media;
920 }
921
922 public T getEntity() {
923 return entity;
924 }
925 public List<Media> getMedia() {
926 return media;
927 }
928
929 /**
930 * @param addTaxonomicChildrenMedia
931 */
932 public void setMedia(List<Media> media) {
933 this.media = media;
934
935 }
936 }
937
938 // ---------------------- code snippet preserved for possible later use --------------------
939 // @RequestMapping(
940 // value = {"//*/portal/taxon/*/descriptions"}, // mapped as absolute path, see CdmAntPathMatcher
941 // method = RequestMethod.GET)
942 // public List<TaxonDescription> doGetDescriptionsbyFeatureTree(HttpServletRequest request, HttpServletResponse response)throws IOException {
943 // TaxonBase tb = getCdmBase(request, response, null, Taxon.class);
944 // if(tb instanceof Taxon){
945 // //T O D O this is a quick and dirty implementation -> generalize
946 // UUID featureTreeUuid = readValueUuid(request, termTreeUuidPattern);
947 //
948 // FeatureTree featureTree = descriptionService.getFeatureTreeByUuid(featureTreeUuid);
949 // Pager<TaxonDescription> p = descriptionService.getTaxonDescriptions((Taxon)tb, null, null, null, null, TAXONDESCRIPTION_INIT_STRATEGY);
950 // List<TaxonDescription> descriptions = p.getRecords();
951 //
952 // if(!featureTree.isDescriptionSeparated()){
953 //
954 // TaxonDescription superDescription = TaxonDescription.NewInstance();
955 // //put all descriptionElements in superDescription and make it invisible
956 // for(TaxonDescription description: descriptions){
957 // for(DescriptionElementBase element: description.getElements()){
958 // superDescription.addElement(element);
959 // }
960 // }
961 // List<TaxonDescription> separatedDescriptions = new ArrayList<TaxonDescription>(descriptions.size());
962 // separatedDescriptions.add(superDescription);
963 // return separatedDescriptions;
964 // }else{
965 // return descriptions;
966 // }
967 // } else {
968 // response.sendError(HttpServletResponse.SC_NOT_FOUND, "invalid type; Taxon expected but " + tb.getClass().getSimpleName() + " found.");
969 // return null;
970 // }
971 // }
972
973 }