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
.name
.Registration
;
41 import eu
.etaxonomy
.cdm
.model
.name
.RegistrationStatus
;
42 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
43 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
44 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
45 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
46 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
47 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
48 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceType
;
49 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.EntityInitStrategy
;
50 import eu
.etaxonomy
.cdm
.persistence
.dao
.initializer
.IBeanInitializer
;
51 import eu
.etaxonomy
.cdm
.persistence
.hibernate
.permission
.CRUD
;
52 import eu
.etaxonomy
.cdm
.persistence
.query
.MatchMode
;
53 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
54 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
.SortOrder
;
57 * Provides RegistrationDTOs and RegistrationWorkingsets for Registrations in the database.
60 * @author a.kohlbecker
64 @Service("registrationWorkingSetService")
65 @Transactional(readOnly
=true)
66 public class RegistrationWorkingSetService
implements IRegistrationWorkingSetService
{
68 public static final EntityInitStrategy REGISTRATION_DTO_INIT_STRATEGY
= new EntityInitStrategy(Arrays
.asList(new String
[]{
71 "typeDesignations.typeStatus",
72 "typeDesignations.typifiedNames.typeDesignations", // important !!
73 "typeDesignations.typeSpecimen",
74 "typeDesignations.typeName.$",
75 "typeDesignations.citation",
76 "typeDesignations.annotations", // needed for AnnotatableEntity.clone() in DerivedUnitConverter.copyPropertiesTo
77 "typeDesignations.markers", // needed for AnnotatableEntity.clone() in DerivedUnitConverter.copyPropertiesTo
78 "typeDesignations.registrations", // DerivedUnitConverter.copyPropertiesTo(TARGET n)
82 "name.nomenclaturalReference",
84 "name.homotypicalGroup.typifiedNames",
86 "name.typeDesignations", // important !!"
91 .extend("typeDesignations.citation", ReferenceEllypsisFormatter
.INIT_STRATEGY
, false)
92 .extend("name.nomenclaturalReference", ReferenceEllypsisFormatter
.INIT_STRATEGY
, false);
94 public EntityInitStrategy DERIVEDUNIT_INIT_STRATEGY
= new EntityInitStrategy(Arrays
.asList(new String
[]{
95 "*", // initialize all related entities to allow DerivedUnit conversion, see DerivedUnitConverter.copyPropertiesTo()
97 "derivedFrom.type", // TODO remove?
98 "derivedFrom.originals.derivationEvents", // important!!
99 "specimenTypeDesignations.typifiedNames.typeDesignations", // important!!
100 "mediaSpecimen.sources.citation",
101 "collection.institute"// see CollectionCaptionGenerator
102 })).extend("mediaSpecimen.sources.citation", ReferenceEllypsisFormatter
.INIT_STRATEGY
, false);
104 public List
<String
> FIELDUNIT_INIT_STRATEGY
= Arrays
.asList(new String
[]{
106 "annotations.*", // * is needed as log as we are using a table in FilterableAnnotationsField
108 "gatheringEvent.country",
109 "gatheringEvent.collectingAreas",
110 "gatheringEvent.actor",
111 "gatheringEvent.exactLocation.$",
112 "derivationEvents.derivatives", // important, otherwise the DerivedUnits are not included into the graph of initialized entities!!!
116 public static final List
<String
> BLOCKING_REGISTRATION_INIT_STRATEGY
= Arrays
.asList(new String
[]{
118 "blockedBy.blockedBy",
120 "blockedBy.typeDesignations.typeStatus",
121 // "typeDesignations.typifiedNames.typeDesignations", // important !?
122 // "blockedBy.name.$",
123 "blockedBy.name.nomenclaturalReference.authorship",
124 "blockedBy.name.nomenclaturalReference.inReference",
125 "blockedBy.name.rank",
127 "blockedBy.institution",
134 private static final int PAGE_SIZE
= 50;
136 private static final Logger logger
= Logger
.getLogger(RegistrationWorkingSetService
.class);
139 @Qualifier("cdmRepository")
140 private CdmRepository repo
;
143 private UserHelper userHelper
;
146 protected IBeanInitializer defaultBeanInitializer
;
148 public RegistrationWorkingSetService() {
154 * @param id the Registration entity id
158 public RegistrationDTO
loadDtoById(Integer id
) {
159 Registration reg
= repo
.getRegistrationService().load(id
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
160 inititializeSpecimen(reg
);
161 return new RegistrationDTO(reg
);
166 * @param id the Registration entity id
170 public RegistrationDTO
loadDtoByUuid(UUID uuid
) {
171 Registration reg
= repo
.getRegistrationService().load(uuid
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
172 inititializeSpecimen(reg
);
173 return new RegistrationDTO(reg
);
177 public Pager
<RegistrationDTO
> pageDTOs(String identifier
, Integer pageIndex
, Integer pageSize
) throws IOException
{
179 Pager
<Registration
> regPager
= repo
.getRegistrationService().pageByIdentifier(identifier
, pageIndex
, pageSize
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
180 return convertToDTOPager(regPager
);
189 public Pager
<RegistrationDTO
> convertToDTOPager(Pager
<Registration
> regPager
) {
190 return new DefaultPagerImpl
<RegistrationDTO
>(regPager
.getCurrentIndex(), regPager
.getCount(), regPager
.getPageSize(), makeDTOs(regPager
.getRecords()));
195 public Pager
<RegistrationDTO
> pageDTOs(Integer pageSize
, Integer pageIndex
) {
197 return pageDTOs((UUID
)null, null, null, null, null, null, pageSize
, pageIndex
, null);
202 * @param submitterUuid
203 * Filter by the uuid of the {@link User} associated with the Registration as <code>Registration.submitter</code>
204 * @param includedStatus
205 * Filter by one or more {@link RegistrationStatus}. Multiple status will be combined with OR. In case the current user
206 * is not authenticated (i.e. the authentication is anonymous) the includedStatus will be set to {@link RegistrationStatus#PUBLISHED}
207 * to protect all other Registrations from being undisclosed.
208 * @param identifierFilterPattern
209 * Filter by the {@link Registration#getIdentifier() Registration.identifier}.
210 * The method matches Registrations which contain the the passed pattern in the identifier.
211 * @param taxonNameFilterPattern
212 * The method matches Registrations which contain the the passed pattern in the
213 * {@link Registration#getName() Registration.name}
214 * @param typeDesignationStatusUuids
215 * Filter by one or more {@link eu.etaxonomy.cdm.model.name.SpecimenTypeDesignationStatus} or {@link eu.etaxonomy.cdm.model.name.NameTypeDesignationStatus}.
216 * Multiple status will be combined with OR.
224 public Pager
<RegistrationDTO
> pageDTOs(UUID submitterUuid
, Collection
<RegistrationStatus
> includedStatus
, String identifierFilterPattern
,
225 String taxonNameFilterPattern
, String referenceFilterPattern
, Collection
<UUID
> typeDesignationStatusUuids
,
226 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
){
228 if(pageSize
== null){
229 pageSize
= PAGE_SIZE
;
232 if(orderHints
== null){
233 orderHints
= Arrays
.asList(new OrderHint("identifier", SortOrder
.ASCENDING
));
236 Pager
<Registration
> pager
= repo
.getRegistrationService().page(submitterUuid
, includedStatus
,
237 identifierFilterPattern
, taxonNameFilterPattern
,
238 referenceFilterPattern
, typeDesignationStatusUuids
, PAGE_SIZE
, pageIndex
, orderHints
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
240 Pager
<RegistrationDTO
> dtoPager
= convertToDTOPager(pager
);
241 if(logger
.isDebugEnabled()){
242 logger
.debug(String
.format("pageDTOs() pageIndex: $1%d, pageSize: $2%d, includedStatusUuids: $3%s, typeDesignationStatusUuids: $4%s, taxonNameFilterPattern: $5%s, submitterUuid: $6%s",
243 pageIndex
, pageSize
, includedStatus
, identifierFilterPattern
, taxonNameFilterPattern
, submitterUuid
));
244 logger
.debug("pageDTOs() result: " + pager
.toString());
251 public Pager
<RegistrationDTO
> findInTaxonGraph(UUID submitterUuid
, Collection
<RegistrationStatus
> includedStatus
,
252 String taxonNameFilterPattern
, MatchMode matchMode
,
253 Integer pageSize
, Integer pageIndex
, List
<OrderHint
> orderHints
) {
255 Pager
<Registration
> regPager
= repo
.getRegistrationService().pageTaxomicInclusion(null, includedStatus
,
256 taxonNameFilterPattern
, matchMode
,
257 pageSize
, pageIndex
, orderHints
, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
259 return convertToDTOPager(regPager
);
265 * @throws RegistrationValidationException
268 public RegistrationWorkingSet
loadWorkingSetByReferenceUuid(UUID referenceUuid
, boolean resolveSections
) throws RegistrationValidationException
, PermissionDeniedException
{
270 Reference reference
= repo
.getReferenceService().load(referenceUuid
); // needed to use load to avoid the problem described in #7331
272 reference
= resolveSection(reference
);
275 checkPermissions(reference
);
277 Pager
<Registration
> pager
= repo
.getRegistrationService().page(Optional
.of(reference
), null, null, null, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
279 /* for debugging https://dev.e-taxonomy.eu/redmine/issues/7331 */
280 // debugIssue7331(pager);
281 RegistrationWorkingSet registrationWorkingSet
;
282 if(pager
.getCount() > 0) {
283 registrationWorkingSet
= new RegistrationWorkingSet(makeDTOs(pager
.getRecords()));
285 registrationWorkingSet
= new RegistrationWorkingSet(reference
);
287 return registrationWorkingSet
;
294 private void checkPermissions(Reference reference
) throws PermissionDeniedException
{
296 boolean permissionDenied
= isPermissionDenied(reference
);
297 if(permissionDenied
) {
298 throw new PermissionDeniedException("Access to the workingset is denied for the current user.");
307 public boolean isPermissionDenied(Reference reference
) {
308 boolean permissionDenied
= false;
309 if(!checkReferencePublished(reference
)){
310 permissionDenied
= !userHelper
.userHasPermission(reference
, CRUD
.UPDATE
);
312 return permissionDenied
;
320 public boolean checkReferencePublished(Reference reference
) {
322 if(reference
.getDatePublished() == null){
325 Partial pubPartial
= null;
326 if(reference
.getDatePublished().getStart() != null){
327 pubPartial
= reference
.getDatePublished().getStart();
329 pubPartial
= reference
.getDatePublished().getEnd();
331 if(pubPartial
== null){
332 return !reference
.getDatePublished().getFreeText().isEmpty();
335 DateTime nowLocal
= new DateTime();
336 //LocalDateTime nowUTC = nowLocal.withZone(DateTimeZone.UTC).toLocalDateTime();
338 DateTime pubDateTime
= pubPartial
.toDateTime(null);
339 return nowLocal
.isAfter(pubDateTime
);
348 protected Reference
resolveSection(Reference reference
) {
349 repo
.getReferenceService().load(reference
.getUuid(), Arrays
.asList(new String
[]{"inReference"})); // needed to avoid the problem described in #7331
350 if(reference
.isOfType(ReferenceType
.Section
) && reference
.getInReference() != null) {
351 reference
= reference
.getInReference();
358 * @throws RegistrationValidationException
361 public RegistrationWorkingSet
loadWorkingSetByReferenceID(Integer referenceID
, boolean resolveSections
) throws RegistrationValidationException
, PermissionDeniedException
{
363 Reference reference
= repo
.getReferenceService().find(referenceID
);
365 reference
= resolveSection(reference
);
368 checkPermissions(reference
);
370 repo
.getReferenceService().load(reference
.getUuid()); // needed to avoid the problem described in #7331
372 Pager
<Registration
> pager
= repo
.getRegistrationService().page(Optional
.of(reference
), null, null, null, REGISTRATION_DTO_INIT_STRATEGY
.getPropertyPaths());
374 /* for debugging https://dev.e-taxonomy.eu/redmine/issues/7331 */
375 // debugIssue7331(pager);
377 return new RegistrationWorkingSet(makeDTOs(pager
.getRecords()));
384 @SuppressWarnings("unused")
385 private void debugIssue7331(Pager
<Registration
> pager
) {
386 for(Registration reg
: pager
.getRecords()){
387 if(reg
.getName() != null && reg
.getName().getNomenclaturalReference().getAuthorship() != null){
388 Reference ref
= reg
.getName().getNomenclaturalReference();
389 if(!Hibernate
.isInitialized(ref
.getAuthorship())){
390 logger
.error("UNINITIALIZED");
393 logger
.debug("NO AUTHORS");
399 public Set
<RegistrationDTO
> loadBlockingRegistrations(UUID blockedRegistrationUuid
){
401 Registration registration
= repo
.getRegistrationService().load(blockedRegistrationUuid
, BLOCKING_REGISTRATION_INIT_STRATEGY
);
402 Set
<Registration
> registrations
= registration
.getBlockedBy();
404 Set
<RegistrationDTO
> blockingSet
= new HashSet
<>();
405 for(Registration reg
: registrations
){
406 blockingSet
.add(new RegistrationDTO(reg
));
416 public List
<RegistrationDTO
> makeDTOs(Collection
<Registration
> regs
) {
417 initializeSpecimens(regs
);
418 List
<RegistrationDTO
> dtos
= new ArrayList
<>(regs
.size());
419 regs
.forEach(reg
-> {dtos
.add(new RegistrationDTO(reg
));});
427 public void initializeSpecimens(Collection
<Registration
> regs
) {
428 for(Registration reg
: regs
){
429 inititializeSpecimen(reg
);
438 public void inititializeSpecimen(Registration reg
) {
440 for(TypeDesignationBase
<?
> td
: reg
.getTypeDesignations()){
441 if(td
instanceof SpecimenTypeDesignation
){
443 DerivedUnit derivedUnit
= ((SpecimenTypeDesignation
) td
).getTypeSpecimen();
444 @SuppressWarnings("rawtypes")
445 Set
<SpecimenOrObservationBase
> sobs
= new HashSet
<>();
446 sobs
.add(HibernateProxyHelper
.deproxy(derivedUnit
));
448 while(sobs
!= null && !sobs
.isEmpty()){
449 @SuppressWarnings("rawtypes")
450 Set
<SpecimenOrObservationBase
> nextSobs
= null;
451 for(@SuppressWarnings("rawtypes") SpecimenOrObservationBase sob
: sobs
){
452 sob
= HibernateProxyHelper
.deproxy(sob
);
456 if(DerivedUnit
.class.isAssignableFrom(sob
.getClass())) {
457 defaultBeanInitializer
.initialize(sob
, DERIVEDUNIT_INIT_STRATEGY
.getPropertyPaths());
458 nextSobs
= ((DerivedUnit
)sob
).getOriginals();
460 if(sob
instanceof FieldUnit
){
461 defaultBeanInitializer
.initialize(sob
, FIELDUNIT_INIT_STRATEGY
);