Merge branch 'develop' into termSearch
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / RegistrationServiceImpl.java
1 /**
2 * Copyright (C) 2017 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.api.service;
10
11 import java.io.IOException;
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Optional;
19 import java.util.Set;
20 import java.util.UUID;
21 import java.util.stream.Collectors;
22
23 import org.springframework.beans.factory.annotation.Autowired;
24 import org.springframework.security.core.Authentication;
25 import org.springframework.stereotype.Service;
26 import org.springframework.transaction.annotation.Transactional;
27
28 import eu.etaxonomy.cdm.api.service.idminter.IdentifierMinter.Identifier;
29 import eu.etaxonomy.cdm.api.service.idminter.RegistrationIdentifierMinter;
30 import eu.etaxonomy.cdm.api.service.pager.Pager;
31 import eu.etaxonomy.cdm.api.service.pager.impl.AbstractPagerImpl;
32 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
33 import eu.etaxonomy.cdm.api.service.taxonGraph.ITaxonGraphService;
34 import eu.etaxonomy.cdm.api.utility.UserHelper;
35 import eu.etaxonomy.cdm.model.common.User;
36 import eu.etaxonomy.cdm.model.name.Registration;
37 import eu.etaxonomy.cdm.model.name.RegistrationStatus;
38 import eu.etaxonomy.cdm.model.name.TaxonName;
39 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
40 import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
41 import eu.etaxonomy.cdm.model.reference.Reference;
42 import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
43 import eu.etaxonomy.cdm.persistence.dao.name.IRegistrationDao;
44 import eu.etaxonomy.cdm.persistence.hibernate.permission.Operation;
45 import eu.etaxonomy.cdm.persistence.query.MatchMode;
46 import eu.etaxonomy.cdm.persistence.query.OrderHint;
47
48 /**
49 * @author a.kohlbecker
50 * @since May 2, 2017
51 *
52 */
53 @Service
54 @Transactional(readOnly = true)
55 public class RegistrationServiceImpl extends AnnotatableServiceBase<Registration, IRegistrationDao>
56 implements IRegistrationService {
57
58 /**
59 * {@inheritDoc}
60 */
61 @Autowired
62 @Override
63 protected void setDao(IRegistrationDao dao) {
64 this.dao = dao;
65 }
66
67 @Autowired(required=false)
68 private RegistrationIdentifierMinter minter;
69
70 @Autowired
71 private UserHelper userHelper;
72
73 @Autowired
74 private INameService nameService;
75
76 @Autowired
77 private ITaxonGraphService taxonGraphService;
78
79
80
81
82 /**
83 * {@inheritDoc}
84 */
85 @Override
86 @Transactional(readOnly = true)
87 public Pager<Registration> page(Optional<Reference> reference, Collection<RegistrationStatus> includedStatus,
88 Integer pageSize, Integer pageIndex, List<String> propertyPaths) {
89
90 if( !userHelper.userIsAutheticated() || userHelper.userIsAnnonymous() ) {
91 includedStatus = Arrays.asList(RegistrationStatus.PUBLISHED);
92 }
93
94 long numberOfResults = dao.count(reference, includedStatus);
95
96 List<Registration> results = new ArrayList<>();
97 Integer [] limitStart = AbstractPagerImpl.limitStartforRange(numberOfResults, pageIndex, pageSize);
98 if(limitStart != null) {
99 results = dao.list(reference, includedStatus, limitStart[0], limitStart[1], propertyPaths);
100 }
101
102 return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
103 }
104
105 /**
106 * {@inheritDoc}
107 */
108 @Override
109 @Transactional(readOnly = true)
110 public Pager<Registration> page(User submitter, Collection<RegistrationStatus> includedStatus,
111 String identifierFilterPattern, String taxonNameFilterPattern, Set<TypeDesignationStatusBase> typeDesignationStatus,
112 Integer pageSize, Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
113
114 List<Restriction<? extends Object>> restrictions = new ArrayList<>();
115
116 if( !userHelper.userIsAutheticated() || userHelper.userIsAnnonymous() ) {
117 includedStatus = Arrays.asList(RegistrationStatus.PUBLISHED);
118 }
119
120 if(submitter != null){
121 restrictions.add(new Restriction<>("submitter", MatchMode.EXACT, submitter));
122 }
123 if(includedStatus != null && !includedStatus.isEmpty()){
124 restrictions.add(new Restriction<>("status", MatchMode.EXACT, includedStatus.toArray(new RegistrationStatus[includedStatus.size()])));
125 }
126 if(identifierFilterPattern != null){
127 restrictions.add(new Restriction<>("identifier", MatchMode.LIKE, identifierFilterPattern));
128 }
129 if(taxonNameFilterPattern != null){
130 restrictions.add(new Restriction<>("name.titleCache", MatchMode.LIKE, taxonNameFilterPattern));
131 }
132 if(typeDesignationStatus != null){
133 restrictions.add(new Restriction<>("typeDesignations.typeStatus", null, typeDesignationStatus.toArray(new TypeDesignationStatusBase[typeDesignationStatus.size()])));
134 }
135
136 long numberOfResults = dao.count(Registration.class, restrictions);
137
138 List<Registration> results = new ArrayList<>();
139 Integer [] limitStart = AbstractPagerImpl.limitStartforRange(numberOfResults, pageIndex, pageSize);
140 if(limitStart != null) {
141 results = dao.list(Registration.class, restrictions, limitStart[0], limitStart[1], orderHints, propertyPaths);
142 }
143
144 return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
145 }
146
147 @Override
148 @Transactional(readOnly = true)
149 public Pager<Registration> page(UUID submitterUuid, Collection<RegistrationStatus> includedStatus,
150 String identifierFilterPattern, String taxonNameFilterPattern, Collection<UUID> typeDesignationStatusUuids,
151 Integer pageSize, Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
152
153 List<Restriction<? extends Object>> restrictions = new ArrayList<>();
154
155 if( !userHelper.userIsAutheticated() || userHelper.userIsAnnonymous() ) {
156 includedStatus = Arrays.asList(RegistrationStatus.PUBLISHED);
157 }
158
159 if(submitterUuid != null){
160 restrictions.add(new Restriction<>("submitter.uuid", null, submitterUuid));
161 }
162 if(includedStatus != null && !includedStatus.isEmpty()){
163 restrictions.add(new Restriction<>("status", null, includedStatus.toArray(new RegistrationStatus[includedStatus.size()])));
164 }
165 if(identifierFilterPattern != null){
166 restrictions.add(new Restriction<>("identifier", MatchMode.LIKE, identifierFilterPattern));
167 }
168 if(taxonNameFilterPattern != null){
169 restrictions.add(new Restriction<>("name.titleCache", MatchMode.LIKE, taxonNameFilterPattern));
170 }
171 if(typeDesignationStatusUuids != null){
172 restrictions.add(new Restriction<>("typeDesignations.typeStatus.uuid", null, typeDesignationStatusUuids.toArray(new UUID[typeDesignationStatusUuids.size()])));
173 }
174
175 long numberOfResults = dao.count(Registration.class, restrictions);
176
177 List<Registration> results = new ArrayList<>();
178 if(pageIndex == null){
179 pageIndex = 0;
180 }
181 Integer [] limitStart = AbstractPagerImpl.limitStartforRange(numberOfResults, pageIndex, pageSize);
182 if(limitStart != null) {
183 results = dao.list(Registration.class, restrictions, limitStart[0], limitStart[1], orderHints, propertyPaths);
184 }
185
186 return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
187 }
188
189 @Override
190 @Transactional(readOnly = true)
191 public Pager<Registration> pageTaxomicInclusion(UUID submitterUuid, Collection<RegistrationStatus> includedStatus,
192 String taxonNameFilterPattern, MatchMode matchMode,
193 Integer pageSize, Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
194
195 List<TaxonName> includedNames = taxonGraphService.listIncludedNames(taxonNameFilterPattern, matchMode);
196 Set<UUID> includedNamesUuids = includedNames.stream().map(TaxonName::getUuid).collect(Collectors.toSet());
197
198 if(includedNames.size() > 0){
199 return page(submitterUuid, includedStatus, includedNamesUuids, pageSize, pageIndex, orderHints, propertyPaths);
200 } else {
201 return new DefaultPagerImpl<>(pageIndex, 0l, pageSize, new ArrayList<Registration>());
202 }
203 }
204
205 @Override
206 @Transactional(readOnly = true)
207 public Pager<Registration> page(UUID submitterUuid, Collection<RegistrationStatus> includedStatus,
208 Collection<UUID> taxonNameUUIDs,
209 Integer pageSize, Integer pageIndex, List<OrderHint> orderHints, List<String> propertyPaths) {
210
211 List<Restriction<? extends Object>> restrictions = new ArrayList<>();
212
213 if( !userHelper.userIsAutheticated() || userHelper.userIsAnnonymous() ) {
214 includedStatus = Arrays.asList(RegistrationStatus.PUBLISHED);
215 }
216
217 if(submitterUuid != null){
218 restrictions.add(new Restriction<>("submitter.uuid", null, submitterUuid));
219 }
220 if(includedStatus != null && !includedStatus.isEmpty()){
221 restrictions.add(new Restriction<>("status", null, includedStatus.toArray(new RegistrationStatus[includedStatus.size()])));
222 }
223
224 if(taxonNameUUIDs != null){
225 restrictions.add(new Restriction<>("name.uuid", null , taxonNameUUIDs.toArray(new UUID[taxonNameUUIDs.size()])));
226 }
227
228 long numberOfResults = dao.count(Registration.class, restrictions);
229
230 List<Registration> results = new ArrayList<>();
231 if(pageIndex == null){
232 pageIndex = 0;
233 }
234 Integer [] limitStart = AbstractPagerImpl.limitStartforRange(numberOfResults, pageIndex, pageSize);
235 if(limitStart != null) {
236 results = dao.list(Registration.class, restrictions, limitStart[0], limitStart[1], orderHints, propertyPaths);
237 }
238
239 return new DefaultPagerImpl<>(pageIndex, numberOfResults, pageSize, results);
240 }
241
242 /**
243 * @param identifier
244 * @param validateUniqueness
245 * @param response
246 * @return
247 * @throws IOException
248 */
249 @Override
250 @Transactional(readOnly = true)
251 public Pager<Registration> pageByIdentifier(String identifier, Integer pageIndex, Integer pageSize, List<String> propertyPaths) throws IOException {
252
253 List<Restriction<?>> restrictions = new ArrayList<>();
254 if( !userHelper.userIsAutheticated() || userHelper.userIsAnnonymous() ) {
255 restrictions.add(new Restriction<>("status", null, RegistrationStatus.PUBLISHED));
256 }
257
258 Pager<Registration> regPager = pageByRestrictions(Registration.class, "identifier", identifier, MatchMode.EXACT,
259 restrictions, pageSize, pageIndex, null, propertyPaths);
260
261
262 return regPager;
263 }
264
265 @Override
266 @Transactional(readOnly = true)
267 public Map<UUID, RegistrationStatus> statusByIdentifier(String identifier) throws IOException {
268
269 Pager<Registration> regPager = pageByRestrictions(Registration.class, "identifier", identifier, MatchMode.EXACT,
270 null, null, null, null, Arrays.asList("status"));
271
272 Map<UUID, RegistrationStatus> map = new HashMap<>();
273 for(Registration reg : regPager.getRecords()){
274 map.put(reg.getUuid(), reg.getStatus());
275 }
276
277 return map;
278 }
279
280 /**
281 * {@inheritDoc}
282 */
283 @Override
284 public Registration save(Registration newInstance) {
285 return assureIsPersisted(newInstance);
286 }
287
288 /**
289 * {@inheritDoc}
290 */
291 @Override
292 public UUID saveOrUpdate(Registration transientObject) {
293 transientObject = assureIsPersisted(transientObject);
294 return super.saveOrUpdate(transientObject);
295 }
296
297
298 /**
299 * {@inheritDoc}
300 */
301 @Override
302 public Map<UUID, Registration> save(Collection<Registration> newInstances) {
303 Map<UUID, Registration> regs = new HashMap<>();
304 for(Registration newInstance : newInstances) {
305 Registration reg = save(newInstance);
306 regs.put(reg.getUuid(), reg);
307 }
308 return regs;
309 }
310
311 /**
312 * {@inheritDoc}
313 */
314 @Override
315 public Map<UUID, Registration> saveOrUpdate(Collection<Registration> transientInstances) {
316 Map<UUID, Registration> regs = new HashMap<>();
317 for(Registration transientInstance : transientInstances) {
318 UUID uuid = saveOrUpdate(transientInstance);
319 regs.put(uuid, transientInstance);
320 }
321 return regs;
322 }
323
324 // ============= functionality to be moved into a "RegistrationManagerBean" ==================
325
326
327 /**
328 * Factory Method
329 * TODO move into RegistrationFactory
330 *
331 * @return a new Registration instance with submitter set to the current authentications principal
332 */
333 @Override
334 public Registration newRegistration() {
335
336 Registration reg = Registration.NewInstance(
337 null,
338 null,
339 null,
340 null);
341 Authentication authentication = userHelper.getAuthentication();
342 reg.setSubmitter((User)authentication.getPrincipal());
343 return reg;
344 }
345
346 /**
347 * @param taxonNameId
348 * @return
349 */
350 @Override
351 @Transactional(readOnly=false)
352 public Registration createRegistrationForName(UUID taxonNameUuid) {
353
354 Registration reg = Registration.NewInstance(
355 null,
356 null,
357 taxonNameUuid != null ? nameService.load(taxonNameUuid, Arrays.asList("nomenclaturalReference.inReference")) : null,
358 null);
359
360 reg = assureIsPersisted(reg);
361
362 return load(reg.getUuid(), Arrays.asList(new String []{"blockedBy"}));
363 }
364
365 /**
366 * @param typeDesignationTarget
367 */
368 @Override
369 @Transactional(readOnly=false)
370 public Registration assureIsPersisted(Registration reg) {
371
372 if(reg.isPersited()){
373 return reg;
374 }
375
376 prepareForSave(reg);
377 reg = super.save(reg);
378 userHelper.createAuthorityForCurrentUser(reg, Operation.UPDATE, RegistrationStatus.PREPARATION.name());
379
380 return reg;
381 }
382
383 @Override
384 @Transactional(readOnly=false)
385 public void addTypeDesignation(UUID registrationUUID, UUID typeDesignationUuid){
386
387 // load the typeDesignations with the registration so that typified names can not be twice in detached sessions
388 // otherwise multiple representation problems might occur
389 Registration registration = load(registrationUUID, Arrays.asList("typeDesignations"));
390 if(registration == null){
391 registration = newRegistration();
392 registration = assureIsPersisted(registration);
393 }
394 TypeDesignationBase<?> nameTypeDesignation = nameService.loadTypeDesignation(typeDesignationUuid, Arrays.asList(""));
395 registration.getTypeDesignations().add(nameTypeDesignation);
396 }
397
398 @Override
399 @Transactional(readOnly=false)
400 public void addTypeDesignation(Registration registration, UUID typeDesignationUuid){
401
402 if(registration == null){
403 registration = newRegistration();
404 registration = assureIsPersisted(registration);
405 } else {
406 if(registration.isPersited()){
407 // make sure the the typeDesignations are loaded with the registration so that typified names can not be twice in detached sessions
408 // otherwise multiple representation problems might occur
409 registration.getTypeDesignations();
410 }
411 }
412 TypeDesignationBase<?> nameTypeDesignation = nameService.loadTypeDesignation(typeDesignationUuid, Arrays.asList(""));
413 registration.getTypeDesignations().add(nameTypeDesignation);
414 }
415
416 /**
417 * Sets the registration identifier and submitter in case the registration is not yet persisted.
418 *
419 * @param reg
420 * The Registration to prepare for saving.
421 */
422 private void prepareForSave(Registration reg) {
423
424 if(!reg.isPersited()){
425 if(minter != null){
426 Identifier<String> identifiers = minter.mint();
427 if(identifiers.getIdentifier() == null){
428 throw new RuntimeException("RegistrationIdentifierMinter configuration incomplete.");
429 }
430 reg.setIdentifier(identifiers.getIdentifier());
431 reg.setSpecificIdentifier(identifiers.getLocalId());
432 }
433 Authentication authentication = userHelper.getAuthentication();
434 reg.setSubmitter((User)authentication.getPrincipal());
435 }
436 }
437
438 /**
439 * @param name
440 */
441 @Override
442 public boolean checkRegistrationExistsFor(TaxonName name) {
443
444 for(Registration reg : name.getRegistrations()){
445 if(minter != null){
446 if(minter.isFromOwnRegistration(reg.getIdentifier())){
447 return true;
448 }
449 } else {
450 return true; // first registrations wins as we can't distinguish them without a minter.
451 }
452 }
453 return false;
454 }
455
456
457
458 // =============================================================================================
459
460
461 }