2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.api
.service
.registration
;
11 import java
.io
.IOException
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Arrays
;
14 import java
.util
.Collection
;
15 import java
.util
.HashSet
;
16 import java
.util
.List
;
17 import java
.util
.Optional
;
19 import java
.util
.UUID
;
21 import org
.apache
.log4j
.Logger
;
22 import org
.hibernate
.Hibernate
;
23 import org
.joda
.time
.DateTime
;
24 import org
.joda
.time
.Partial
;
25 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
26 import org
.springframework
.beans
.factory
.annotation
.Qualifier
;
27 import org
.springframework
.stereotype
.Service
;
28 import org
.springframework
.transaction
.annotation
.Transactional
;
30 import eu
.etaxonomy
.cdm
.api
.application
.CdmRepository
;
31 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RegistrationDTO
;
32 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RegistrationWorkingSet
;
33 import eu
.etaxonomy
.cdm
.api
.service
.exception
.RegistrationValidationException
;
34 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
35 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
36 import eu
.etaxonomy
.cdm
.api
.utility
.UserHelper
;
37 import eu
.etaxonomy
.cdm
.database
.PermissionDeniedException
;
38 import eu
.etaxonomy
.cdm
.format
.ReferenceEllypsisFormatter
;
39 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
40 import eu
.etaxonomy
.cdm
.model
.common
.User
;
41 import eu
.etaxonomy
.cdm
.model
.name
.Registration
;
42 import eu
.etaxonomy
.cdm
.model
.name
.RegistrationStatus
;
43 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
44 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
45 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationStatusBase
;
46 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
47 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
48 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
49 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
50 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceType
;
51 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.EntityInitStrategy
;
52 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
53 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.CRUD
;
54 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
55 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
56 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
59 * Provides RegistrationDTOs and RegistrationWorkingsets for Registrations in the database.
62 * @author a.kohlbecker
66 @Service("registrationWorkingSetService")
67 @Transactional(readOnly
=true)
68 public class RegistrationWorkingSetService
implements IRegistrationWorkingSetService
{
70 public static final EntityInitStrategy REGISTRATION_DTO_INIT_STRATEGY
= new EntityInitStrategy(Arrays
.asList(new String
[]{
73 "typeDesignations.typeStatus",
74 "typeDesignations.typifiedNames.typeDesignations", // important !!
75 "typeDesignations.typeSpecimen",
76 "typeDesignations.typeName.$",
77 "typeDesignations.citation",
78 "typeDesignations.annotations", // needed for AnnotatableEntity.clone() in DerivedUnitConverter.copyPropertiesTo
79 "typeDesignations.markers", // needed for AnnotatableEntity.clone() in DerivedUnitConverter.copyPropertiesTo
80 "typeDesignations.registrations", // DerivedUnitConverter.copyPropertiesTo(TARGET n)
84 "name.nomenclaturalReference",
86 "name.homotypicalGroup.typifiedNames",
88 "name.typeDesignations", // important !!"
93 .extend("typeDesignations.citation", ReferenceEllypsisFormatter
.INIT_STRATEGY
, false)
94 .extend("name.nomenclaturalReference", ReferenceEllypsisFormatter
.INIT_STRATEGY
, false);
96 public EntityInitStrategy DERIVEDUNIT_INIT_STRATEGY
= new EntityInitStrategy(Arrays
.asList(new String
[]{
97 "*", // initialize all related entities to allow DerivedUnit conversion, see DerivedUnitConverter.copyPropertiesTo()
99 "derivedFrom.type", // TODO remove?
100 "derivedFrom.originals.derivationEvents", // important!!
101 "specimenTypeDesignations.typifiedNames.typeDesignations", // important!!
102 "mediaSpecimen.sources.citation",
103 "collection.institute"// see CollectionCaptionGenerator
104 })).extend("mediaSpecimen.sources.citation", ReferenceEllypsisFormatter
.INIT_STRATEGY
, false);
106 public List
<String
> FIELDUNIT_INIT_STRATEGY
= Arrays
.asList(new String
[]{
108 "annotations.*", // * is needed as log as we are using a table in FilterableAnnotationsField
110 "gatheringEvent.country",
111 "gatheringEvent.collectingAreas",
112 "gatheringEvent.actor",
113 "gatheringEvent.exactLocation.$",
114 "derivationEvents.derivatives", // important, otherwise the DerivedUnits are not included into the graph of initialized entities!!!
118 public static final List
<String
> BLOCKING_REGISTRATION_INIT_STRATEGY
= Arrays
.asList(new String
[]{
120 "blockedBy.blockedBy",
122 "blockedBy.typeDesignations.typeStatus",
123 // "typeDesignations.typifiedNames.typeDesignations", // important !?
124 // "blockedBy.name.$",
125 "blockedBy.name.nomenclaturalReference.authorship",
126 "blockedBy.name.nomenclaturalReference.inReference",
127 "blockedBy.name.rank",
129 "blockedBy.institution",
136 private static final int PAGE_SIZE
= 50;
138 private static final Logger logger
= Logger
.getLogger(RegistrationWorkingSetService
.class);
141 @Qualifier("cdmRepository")
142 private CdmRepository repo
;
145 private UserHelper userHelper
;
148 protected IBeanInitializer defaultBeanInitializer
;
150 public RegistrationWorkingSetService() {
156 * @param id the Registration entity id
160 public RegistrationDTO
loadDtoById(Integer id
) {
161 Registration reg
= repo
.getRegistrationService().load(id
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
162 inititializeSpecimen(reg
);
163 return new RegistrationDTO(reg
);
168 * @param id the Registration entity id
172 public RegistrationDTO
loadDtoByUuid(UUID uuid
) {
173 Registration reg
= repo
.getRegistrationService().load(uuid
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
174 inititializeSpecimen(reg
);
175 return new RegistrationDTO(reg
);
179 public Pager
<RegistrationDTO
> pageDTOs(String identifier
, Integer pageIndex
, Integer pageSize
) throws IOException
{
181 Pager
<Registration
> regPager
= repo
.getRegistrationService().pageByIdentifier(identifier
, pageIndex
, pageSize
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
182 return convertToDTOPager(regPager
);
191 public Pager
<RegistrationDTO
> convertToDTOPager(Pager
<Registration
> regPager
) {
192 return new DefaultPagerImpl
<RegistrationDTO
>(regPager
.getCurrentIndex(), regPager
.getCount(), regPager
.getPageSize(), makeDTOs(regPager
.getRecords()));
197 public Pager
<RegistrationDTO
> pageDTOs(Integer pageSize
, Integer pageIndex
) {
199 return pageDTOs((User
)null, null, null, null, null, pageSize
, pageIndex
, null);
206 public Pager
<RegistrationDTO
> pageDTOs(User submitter
, Collection
<RegistrationStatus
> includedStatus
,
207 String identifierFilterPattern
, String taxonNameFilterPattern
, Set
<TypeDesignationStatusBase
> typeStatusFilter
,
208 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
210 if(pageSize
== null){
211 pageSize
= PAGE_SIZE
;
214 if(orderHints
== null){
215 orderHints
= Arrays
.asList(new OrderHint("identifier", SortOrder
.ASCENDING
));
218 Pager
<Registration
> pager
= repo
.getRegistrationService().page(submitter
, includedStatus
, identifierFilterPattern
, taxonNameFilterPattern
,
219 typeStatusFilter
, PAGE_SIZE
, pageIndex
, orderHints
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
221 Pager
<RegistrationDTO
> dtoPager
= convertToDTOPager(pager
);
222 if(logger
.isDebugEnabled()){
223 logger
.debug(String
.format("pageDTOs() pageIndex: $1%d, pageSize: $2%d, includedStatus: $3%s, identifierFilterPattern: $4%s, taxonNameFilterPattern: $5%s, submitter: $6%s",
224 pageIndex
, pageSize
, includedStatus
, identifierFilterPattern
, taxonNameFilterPattern
, submitter
));
225 logger
.debug("pageDTOs() result: " + pager
.toString());
231 * @param submitterUuid
232 * Filter by the uuid of the {@link User} associated with the Registration as <code>Registration.submitter</code>
233 * @param includedStatus
234 * Filter by one or more {@link RegistrationStatus}. Multiple status will be combined with OR. In case the current user
235 * is not authenticated (i.e. the authentication is anonymous) the includedStatus will be set to {@link RegistrationStatus#PUBLISHED}
236 * to protect all other Registrations from being undisclosed.
237 * @param identifierFilterPattern
238 * Filter by the {@link Registration#getIdentifier() Registration.identifier}.
239 * The method matches Registrations which contain the the passed pattern in the identifier.
240 * @param taxonNameFilterPattern
241 * The method matches Registrations which contain the the passed pattern in the
242 * {@link Registration#getName() Registration.name}
243 * @param typeDesignationStatusUuids
244 * Filter by one or more {@link eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus} or {@link eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus}.
245 * Multiple status will be combined with OR.
252 public Pager
<RegistrationDTO
> pageDTOs(UUID submitterUuid
, Collection
<RegistrationStatus
> includedStatus
, String identifierFilterPattern
,
253 String taxonNameFilterPattern
, String referenceFilterPattern
, Collection
<UUID
> typeDesignationStatusUuids
,
254 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
){
256 if(pageSize
== null){
257 pageSize
= PAGE_SIZE
;
260 if(orderHints
== null){
261 orderHints
= Arrays
.asList(new OrderHint("identifier", SortOrder
.ASCENDING
));
264 Pager
<Registration
> pager
= repo
.getRegistrationService().page(submitterUuid
, includedStatus
,
265 identifierFilterPattern
, taxonNameFilterPattern
,
266 referenceFilterPattern
, typeDesignationStatusUuids
, PAGE_SIZE
, pageIndex
, orderHints
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
268 Pager
<RegistrationDTO
> dtoPager
= convertToDTOPager(pager
);
269 if(logger
.isDebugEnabled()){
270 logger
.debug(String
.format("pageDTOs() pageIndex: $1%d, pageSize: $2%d, includedStatusUuids: $3%s, typeDesignationStatusUuids: $4%s, taxonNameFilterPattern: $5%s, submitterUuid: $6%s",
271 pageIndex
, pageSize
, includedStatus
, identifierFilterPattern
, taxonNameFilterPattern
, submitterUuid
));
272 logger
.debug("pageDTOs() result: " + pager
.toString());
279 public Pager
<RegistrationDTO
> findInTaxonGraph(UUID submitterUuid
, Collection
<RegistrationStatus
> includedStatus
,
280 String taxonNameFilterPattern
, MatchMode matchMode
,
281 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
283 Pager
<Registration
> regPager
= repo
.getRegistrationService().pageTaxomicInclusion(null, includedStatus
,
284 taxonNameFilterPattern
, matchMode
,
285 pageSize
, pageIndex
, orderHints
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
287 return convertToDTOPager(regPager
);
293 * @throws RegistrationValidationException
296 public RegistrationWorkingSet
loadWorkingSetByReferenceUuid(UUID referenceUuid
, boolean resolveSections
) throws RegistrationValidationException
, PermissionDeniedException
{
298 Reference reference
= repo
.getReferenceService().load(referenceUuid
); // needed to use load to avoid the problem described in #7331
300 reference
= resolveSection(reference
);
303 checkPermissions(reference
);
305 Pager
<Registration
> pager
= repo
.getRegistrationService().page(Optional
.of(reference
), null, null, null, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
307 /* for debugging https://dev.e-taxonomy.eu/redmine/issues/7331 */
308 // debugIssue7331(pager);
309 RegistrationWorkingSet registrationWorkingSet
;
310 if(pager
.getCount() > 0) {
311 registrationWorkingSet
= new RegistrationWorkingSet(makeDTOs(pager
.getRecords()));
313 registrationWorkingSet
= new RegistrationWorkingSet(reference
);
315 return registrationWorkingSet
;
322 private void checkPermissions(Reference reference
) throws PermissionDeniedException
{
324 boolean permissionDenied
= isPermissionDenied(reference
);
325 if(permissionDenied
) {
326 throw new PermissionDeniedException("Access to the workingset is denied for the current user.");
335 public boolean isPermissionDenied(Reference reference
) {
336 boolean permissionDenied
= false;
337 if(!checkReferencePublished(reference
)){
338 permissionDenied
= !userHelper
.userHasPermission(reference
, CRUD
.UPDATE
);
340 return permissionDenied
;
348 public boolean checkReferencePublished(Reference reference
) {
350 if(reference
.getDatePublished() == null){
353 Partial pubPartial
= null;
354 if(reference
.getDatePublished().getStart() != null){
355 pubPartial
= reference
.getDatePublished().getStart();
357 pubPartial
= reference
.getDatePublished().getEnd();
359 if(pubPartial
== null){
360 return !reference
.getDatePublished().getFreeText().isEmpty();
363 DateTime nowLocal
= new DateTime();
364 //LocalDateTime nowUTC = nowLocal.withZone(DateTimeZone.UTC).toLocalDateTime();
366 DateTime pubDateTime
= pubPartial
.toDateTime(null);
367 return nowLocal
.isAfter(pubDateTime
);
376 protected Reference
resolveSection(Reference reference
) {
377 repo
.getReferenceService().load(reference
.getUuid(), Arrays
.asList(new String
[]{"inReference"})); // needed to avoid the problem described in #7331
378 if(reference
.isOfType(ReferenceType
.Section
) && reference
.getInReference() != null) {
379 reference
= reference
.getInReference();
386 * @throws RegistrationValidationException
389 public RegistrationWorkingSet
loadWorkingSetByReferenceID(Integer referenceID
, boolean resolveSections
) throws RegistrationValidationException
, PermissionDeniedException
{
391 Reference reference
= repo
.getReferenceService().find(referenceID
);
393 reference
= resolveSection(reference
);
396 checkPermissions(reference
);
398 repo
.getReferenceService().load(reference
.getUuid()); // needed to avoid the problem described in #7331
400 Pager
<Registration
> pager
= repo
.getRegistrationService().page(Optional
.of(reference
), null, null, null, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
402 /* for debugging https://dev.e-taxonomy.eu/redmine/issues/7331 */
403 // debugIssue7331(pager);
405 return new RegistrationWorkingSet(makeDTOs(pager
.getRecords()));
412 @SuppressWarnings("unused")
413 private void debugIssue7331(Pager
<Registration
> pager
) {
414 for(Registration reg
: pager
.getRecords()){
415 if(reg
.getName() != null && reg
.getName().getNomenclaturalReference().getAuthorship() != null){
416 Reference ref
= reg
.getName().getNomenclaturalReference();
417 if(!Hibernate
.isInitialized(ref
.getAuthorship())){
418 logger
.error("UNINITIALIZED");
421 logger
.debug("NO AUTHORS");
427 public Set
<RegistrationDTO
> loadBlockingRegistrations(UUID blockedRegistrationUuid
){
429 Registration registration
= repo
.getRegistrationService().load(blockedRegistrationUuid
, BLOCKING_REGISTRATION_INIT_STRATEGY
);
430 Set
<Registration
> registrations
= registration
.getBlockedBy();
432 Set
<RegistrationDTO
> blockingSet
= new HashSet
<>();
433 for(Registration reg
: registrations
){
434 blockingSet
.add(new RegistrationDTO(reg
));
444 public List
<RegistrationDTO
> makeDTOs(Collection
<Registration
> regs
) {
445 initializeSpecimens(regs
);
446 List
<RegistrationDTO
> dtos
= new ArrayList
<>(regs
.size());
447 regs
.forEach(reg
-> {dtos
.add(new RegistrationDTO(reg
));});
455 public void initializeSpecimens(Collection
<Registration
> regs
) {
456 for(Registration reg
: regs
){
457 inititializeSpecimen(reg
);
466 public void inititializeSpecimen(Registration reg
) {
468 for(TypeDesignationBase
<?
> td
: reg
.getTypeDesignations()){
469 if(td
instanceof SpecimenTypeDesignation
){
471 DerivedUnit derivedUnit
= ((SpecimenTypeDesignation
) td
).getTypeSpecimen();
472 @SuppressWarnings("rawtypes")
473 Set
<SpecimenOrObservationBase
> sobs
= new HashSet
<>();
474 sobs
.add(HibernateProxyHelper
.deproxy(derivedUnit
));
476 while(sobs
!= null && !sobs
.isEmpty()){
477 @SuppressWarnings("rawtypes")
478 Set
<SpecimenOrObservationBase
> nextSobs
= null;
479 for(@SuppressWarnings("rawtypes") SpecimenOrObservationBase sob
: sobs
){
480 sob
= HibernateProxyHelper
.deproxy(sob
);
484 if(DerivedUnit
.class.isAssignableFrom(sob
.getClass())) {
485 defaultBeanInitializer
.initialize(sob
, DERIVEDUNIT_INIT_STRATEGY
.getPropertyPaths());
486 nextSobs
= ((DerivedUnit
)sob
).getOriginals();
488 if(sob
instanceof FieldUnit
){
489 defaultBeanInitializer
.initialize(sob
, FIELDUNIT_INIT_STRATEGY
);