cleanup
[cdm-vaadin.git] / src / main / java / eu / etaxonomy / cdm / vaadin / view / registration / RegistrationWorkingsetPresenter.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.vaadin.view.registration;
10
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.EnumSet;
15 import java.util.HashMap;
16 import java.util.HashSet;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Objects;
20 import java.util.Optional;
21 import java.util.Set;
22 import java.util.Stack;
23 import java.util.UUID;
24
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.springframework.beans.factory.annotation.Autowired;
28 import org.vaadin.spring.events.EventScope;
29 import org.vaadin.spring.events.annotation.EventBusListenerMethod;
30
31 import com.vaadin.spring.annotation.SpringComponent;
32 import com.vaadin.spring.annotation.ViewScope;
33 import com.vaadin.ui.AbstractField;
34 import com.vaadin.ui.Label;
35 import com.vaadin.ui.UI;
36 import com.vaadin.ui.VerticalLayout;
37 import com.vaadin.ui.Window;
38
39 import eu.etaxonomy.cdm.api.service.config.RegistrationStatusTransitions;
40 import eu.etaxonomy.cdm.api.service.dto.RegistrationDTO;
41 import eu.etaxonomy.cdm.api.service.dto.RegistrationWorkingSet;
42 import eu.etaxonomy.cdm.api.service.exception.TypeDesignationSetException;
43 import eu.etaxonomy.cdm.api.service.name.TypeDesignationSet.TypeDesignationSetType;
44 import eu.etaxonomy.cdm.api.service.registration.IRegistrationWorkingSetService;
45 import eu.etaxonomy.cdm.api.util.UserHelper;
46 import eu.etaxonomy.cdm.cache.CdmTransientEntityWithUuidCacher;
47 import eu.etaxonomy.cdm.model.ICdmEntityUuidCacher;
48 import eu.etaxonomy.cdm.model.common.CdmBase;
49 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
50 import eu.etaxonomy.cdm.model.name.Rank;
51 import eu.etaxonomy.cdm.model.name.Registration;
52 import eu.etaxonomy.cdm.model.name.RegistrationStatus;
53 import eu.etaxonomy.cdm.model.name.TaxonName;
54 import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
55 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
56 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
57 import eu.etaxonomy.cdm.model.permission.CRUD;
58 import eu.etaxonomy.cdm.model.reference.Reference;
59 import eu.etaxonomy.cdm.ref.TypedEntityReference;
60 import eu.etaxonomy.cdm.service.CdmBeanItemContainerFactory;
61 import eu.etaxonomy.cdm.service.CdmFilterablePagingProvider;
62 import eu.etaxonomy.cdm.service.CdmFilterablePagingProviderFactory;
63 import eu.etaxonomy.cdm.service.CdmStore;
64 import eu.etaxonomy.cdm.service.IRegistrationWorkflowService;
65 import eu.etaxonomy.cdm.service.UserHelperAccess;
66 import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationItem;
67 import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationStatusFieldInstantiator;
68 import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationStatusSelect;
69 import eu.etaxonomy.cdm.vaadin.event.EditorActionContext;
70 import eu.etaxonomy.cdm.vaadin.event.EditorActionTypeFilter;
71 import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
72 import eu.etaxonomy.cdm.vaadin.event.ReferenceEditorAction;
73 import eu.etaxonomy.cdm.vaadin.event.RegistrationEditorAction;
74 import eu.etaxonomy.cdm.vaadin.event.ShowDetailsEvent;
75 import eu.etaxonomy.cdm.vaadin.event.ShowDetailsEventEntityTypeFilter;
76 import eu.etaxonomy.cdm.vaadin.event.TaxonNameEditorAction;
77 import eu.etaxonomy.cdm.vaadin.event.TypeDesignationSetEditorAction;
78 import eu.etaxonomy.cdm.vaadin.event.registration.RegistrationWorkingsetAction;
79 import eu.etaxonomy.cdm.vaadin.ui.RegistrationUI;
80 import eu.etaxonomy.cdm.vaadin.ui.RegistrationUIDefaults;
81 import eu.etaxonomy.cdm.vaadin.ui.config.TaxonNamePopupEditorConfig;
82 import eu.etaxonomy.cdm.vaadin.util.CdmTitleCacheCaptionGenerator;
83 import eu.etaxonomy.cdm.vaadin.view.name.CachingPresenter;
84 import eu.etaxonomy.cdm.vaadin.view.name.NameTypeDesignationPopupEditor;
85 import eu.etaxonomy.cdm.vaadin.view.name.NameTypeDesignationSetIds;
86 import eu.etaxonomy.cdm.vaadin.view.name.SpecimenTypeDesignationSetIds;
87 import eu.etaxonomy.cdm.vaadin.view.name.SpecimenTypeDesignationSetPopupEditor;
88 import eu.etaxonomy.cdm.vaadin.view.name.TaxonNameEditorPresenter;
89 import eu.etaxonomy.cdm.vaadin.view.name.TaxonNamePopupEditor;
90 import eu.etaxonomy.cdm.vaadin.view.name.TaxonNamePopupEditorMode;
91 import eu.etaxonomy.cdm.vaadin.view.reference.ReferencePopupEditor;
92 import eu.etaxonomy.vaadin.mvp.AbstractPopupEditor;
93 import eu.etaxonomy.vaadin.mvp.AbstractPresenter;
94 import eu.etaxonomy.vaadin.mvp.AbstractView;
95 import eu.etaxonomy.vaadin.mvp.BeanInstantiator;
96 import eu.etaxonomy.vaadin.ui.navigation.NavigationEvent;
97 import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent;
98 import eu.etaxonomy.vaadin.ui.view.DoneWithPopupEvent.Reason;
99 import eu.etaxonomy.vaadin.ui.view.PopupView;
100
101 /**
102 * @author a.kohlbecker
103 * @since Mar 3, 2017
104 */
105 @SpringComponent
106 @ViewScope
107 public class RegistrationWorkingsetPresenter
108 extends AbstractPresenter<RegistrationWorkingsetPresenter,RegistrationWorkingsetView>
109 implements CachingPresenter {
110
111 private static final long serialVersionUID = 2618456456539802265L;
112
113 private static final Logger logger = LogManager.getLogger();
114
115 @Autowired
116 private IRegistrationWorkingSetService regWorkingSetService;
117
118 @Autowired
119 private IRegistrationWorkflowService registrationWorkflowService;
120
121 @Autowired
122 private CdmFilterablePagingProviderFactory pagingProviderFactory;
123
124 @Autowired
125 private CdmBeanItemContainerFactory selectFieldFactory;
126
127 @Autowired
128 private CdmStore cdmStore;
129
130 /**
131 * @return the regWorkingSetService
132 */
133 public IRegistrationWorkingSetService getWorkingSetService() {
134 return regWorkingSetService;
135 }
136
137 private RegistrationWorkingSet workingset;
138
139 /**
140 * Contains the poupeditor which has been opened to start the registration of a new name as long as it has not been saved or canceled.
141 * There can always only be one popup editor for this purpose.
142 */
143 private TaxonNamePopupEditor newNameForRegistrationPopupEditor = null;
144
145 /**
146 * Contains
147 */
148 private List<Registration> newNameBlockingRegistrations = new ArrayList<>();
149
150 /**
151 * TODO is this still needed? The registration UUID should be accessible in the popup editor context,
152 * see findRegistrationInContext()
153 */
154 private Map<NameTypeDesignationPopupEditor, UUID> nameTypeDesignationPopupEditorRegistrationUUIDMap = new HashMap<>();
155
156
157 private ICdmEntityUuidCacher cache;
158
159 private Collection<CdmBase> rootEntities = new HashSet<>();
160
161
162 public RegistrationWorkingsetPresenter() {
163 }
164
165 /**
166 * @param doReload reload the workingset from the persistent storage.
167 * Workingsets which are not yet persisted are preserved.
168 */
169 protected void refreshView(boolean doReload) {
170
171 if(workingset == null){
172 return; // nothing to do
173 }
174 if(doReload){
175 if(logger.isDebugEnabled()){
176 logger.debug("refreshView() - workingset:\n" + workingset.toString());
177 }
178 List<RegistrationDTO> unpersisted = new ArrayList<>();
179 for(RegistrationDTO regDto : workingset.getRegistrationDTOs()){
180 if(!regDto.registration().isPersisted()){
181 unpersisted.add(regDto);
182 }
183 }
184 loadWorkingSet(workingset.getCitationUuid());
185 for(RegistrationDTO regDtoUnpersisted : unpersisted){
186 if(!workingset.getRegistrationDTOs().stream().anyMatch(dto -> dto.getUuid().equals(regDtoUnpersisted.getUuid()))){
187 // only add if the regDtoUnpersisted has not been persisted meanwhile
188 try {
189 workingset.add(regDtoUnpersisted);
190 } catch (TypeDesignationSetException e) {
191 // would never happen here //
192 }
193 }
194 }
195 if(logger.isDebugEnabled()){
196 logger.debug("refreshView() - workingset reloaded:\n" + workingset.toString());
197 }
198 }
199 applyWorkingset();
200 }
201
202 @Override
203 public void handleViewEntered() {
204 super.handleViewEntered();
205 // TODO currently cannot specify type more precisely, see AbstractSelect
206 // FIXME externalize into class file!!!!!!!!!!!!
207 getView().setStatusComponentInstantiator(new RegistrationStatusFieldInstantiator<Object>(){
208
209 private static final long serialVersionUID = 7099181280977511048L;
210
211 @Override
212 public AbstractField<Object> create(RegistrationDTO regDto) {
213
214 // submitters have GrantedAuthorities like REGISTRATION(PREPARATION).[UPDATE]{ab4459eb-3b96-40ba-bfaa-36915107d59e}
215 UserHelper userHelper = UserHelperAccess.userHelper().withCache(getCache());
216 Set<RegistrationStatus> availableStatus = new HashSet<>();
217
218 boolean canChangeStatus = userHelper.userHasPermission(regDto.registration(), CRUD.UPDATE);
219 availableStatus.add(regDto.getStatus());
220 if(canChangeStatus){
221 if(userHelper.userIsAdmin()){
222 availableStatus.addAll(Arrays.asList(RegistrationStatus.values()));
223 } else {
224 availableStatus.addAll(RegistrationStatusTransitions.possibleTransitions(regDto.getStatus()));
225 }
226 }
227
228 RegistrationStatusSelect select = new RegistrationStatusSelect(null, selectFieldFactory.buildEnumTermItemContainer(
229 RegistrationStatus.class,
230 availableStatus.toArray(new RegistrationStatus[availableStatus.size()]))
231 );
232 select.setValue(regDto.getStatus());
233 select.addValueChangeListener(e -> saveRegistrationStatusChange(regDto.getUuid(), e.getProperty().getValue()));
234 select.setEnabled(canChangeStatus);
235 select.setNullSelectionAllowed(false);
236 return select;
237 }
238 });
239
240 loadWorkingSet(getView().getCitationUuid());
241 applyWorkingset();
242
243 }
244
245 private void applyWorkingset(){
246 getView().setWorkingset(workingset);
247 // PagingProviders and CacheGenerator for the existingNameCombobox
248 activateComboboxes();
249 }
250
251 protected void activateComboboxes() {
252 CdmTitleCacheCaptionGenerator<TaxonName> titleCacheGenerator = new CdmTitleCacheCaptionGenerator<TaxonName>();
253 getView().getExistingNameCombobox().setCaptionGenerator(titleCacheGenerator);
254 CdmFilterablePagingProvider<TaxonName, TaxonName> pagingProvider = pagingProviderFactory.taxonNamesWithoutOrthophicIncorrect();
255 getView().getExistingNameCombobox().loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
256 }
257
258 protected void loadWorkingSet(UUID referenceUuid) {
259
260 try {
261 workingset = getWorkingSetService().loadWorkingSetByReferenceUuid(referenceUuid, true);
262 } catch (TypeDesignationSetException error) {
263 logger.error(error);
264 showErrorDialog("Validation Error", error.getMessage());
265 }
266 cache = new CdmTransientEntityWithUuidCacher(this);
267 for(Registration registration : workingset.getRegistrations()) {
268 addRootEntity(registration);
269 }
270 }
271
272 public void showErrorDialog(String errorDialogCaption, String errorMessage) {
273 Window errorDialog = new Window(errorDialogCaption);
274 errorDialog.setModal(true);
275 VerticalLayout subContent = new VerticalLayout();
276 subContent.setMargin(true);
277 errorDialog.setContent(subContent);
278 subContent.addComponent(new Label(errorMessage));
279 UI.getCurrent().addWindow(errorDialog);
280 }
281
282 private void saveRegistrationStatusChange(UUID uuid, Object value) {
283 Registration reg = getRepo().getRegistrationService().load(uuid);
284 if(reg == null){
285 // registration was not yet persisted, ignore
286 return;
287 }
288 if(value != null && value instanceof RegistrationStatus){
289 if(!Objects.equals(value, reg.getStatus())){
290 reg.updateStatusAndDate((RegistrationStatus)value);
291 cdmStore.saveBean(reg, (AbstractView<?,?>)getView());
292 refreshView(true);
293 }
294 } else {
295 // only log here as error
296 logger.error("Ivalid attempt to set RegistrationStatus to " + Objects.toString(value.toString(), "NULL"));
297 }
298 }
299
300 @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
301 public void onReferenceEditorActionAdd(ReferenceEditorAction event) {
302
303 if(!checkFromOwnView(event)){
304 return;
305 }
306
307 ReferencePopupEditor popup = openPopupEditor(ReferencePopupEditor.class, event);
308 popup.withReferenceTypes(RegistrationUIDefaults.PRINTPUB_REFERENCE_TYPES);
309 popup.loadInEditor(null);
310 }
311
312 @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
313 public void onReferenceEditorActionEdit(ReferenceEditorAction event) {
314
315 if(!checkFromOwnView(event)){
316 return;
317 }
318 ReferencePopupEditor popup = openPopupEditor(ReferencePopupEditor.class, event);
319 popup.withReferenceTypes(RegistrationUIDefaults.PRINTPUB_REFERENCE_TYPES);
320 popup.withDeleteButton(true);
321 popup.loadInEditor(event.getEntityUuid());
322 }
323
324 @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
325 public void onRegistrationEditorAction(RegistrationEditorAction event) {
326
327 if(!checkFromOwnView(event)){
328 return;
329 }
330
331 RegistrationPopupEditor popup = openPopupEditor(RegistrationPopupEditor.class, event);
332 popup.loadInEditor(event.getEntityUuid());
333 }
334
335 @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
336 public void onTaxonNameEditorActionEdit(TaxonNameEditorAction event) {
337
338 if(!checkFromOwnView(event)){
339 return;
340 }
341
342 boolean isRegistrationForExistingName = event.getTarget() != null && event.getTarget().equals(getView().getExistingNameCombobox());
343
344 TaxonNamePopupEditor popup = openPopupEditor(TaxonNamePopupEditor.class, event);
345
346 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
347 popup.withDeleteButton(!isRegistrationForExistingName);
348 TaxonNamePopupEditorConfig.configureForNomenclaturalAct(popup);
349 if(isRegistrationForExistingName){
350 // allow saving even if the name parts are not valid
351 // the user will need to fix this in a later step
352 popup.disableMode(TaxonNamePopupEditorMode.VALIDATE_AGAINST_HIGHER_NAME_PART);
353 getView().getAddExistingNameRegistrationButton().setEnabled(false);
354 popup.addDetachListener(ev ->
355 getView().getAddExistingNameRegistrationButton().setEnabled(true)
356 );
357 }
358 popup.loadInEditor(event.getEntityUuid());
359 if(event.hasSource() && event.getSource().isReadOnly()){
360 // avoid resetting read-only to false
361 logger.info("Set popup to read-only as event source is read only");
362 popup.setReadOnly(true);
363 }
364
365 boolean hasNomRef = popup.getBean().getNomenclaturalReference() != null;
366 if(isRegistrationForExistingName){
367 popup.setAllFieldsReadOnly(true);
368 popup.removeStatusMessage(RegistrationUI.CHECK_IN_SEARCH_INDEX);
369
370 if(!hasNomRef){
371 //#10269 for now we do not allow registrations for names with no nom. ref.
372 // Old code was:
373 // // only allow editing the nomenclatural reference, all other
374 // // editing need to be done another way.
375 // // Otherwise we would need to be prepared for creating blocking registrations
376 // // in turn of creation, modification of related taxon names.
377 // popup.disableMode(TaxonNamePopupEditorMode.NOMENCLATURALREFERENCE_SECTION_EDITING_ONLY);
378 // popup.getNomReferenceCombobox().setReadOnly(false);
379 // popup.getNomenclaturalReferenceDetail().setReadOnly(false);
380 // popup.addStatusMessage("The chosen name needs to be completed before it can be used. "
381 // + "Please add the nomenclatural reference and click on \"Save\" to proceed with entering the type of this name.");
382
383 //instead we show status message:
384 // popup.setToCancelOnly();
385 popup.addStatusMessage("<p style='color:red;'><strong>The data entry is aborted "
386 + "due to a data issue that should be fixed "
387 + "by the curator</strong>.<BR>"
388 + "Please send an e-mail with the scientific name "
389 + "to <i>curation@phycobank.org</i></p>");
390 } else {
391 popup.setToSelect(); //sets the save button to "save & select"
392 popup.addStatusMessage("You are about to create a registration for this name. "
393 + "This editor is for reviewing the name only. Therefore, all fields have "
394 + "been switched to read-only state. "
395 + "Click on \"Save\" to proceed with entering the type of this name.");
396 }
397 }
398 }
399
400 @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
401 public void onTaxonNameEditorActionAdd(TaxonNameEditorAction event) {
402
403 if(!checkFromOwnView(event)){
404 return;
405 }
406
407 getView().getAddNewNameRegistrationButton().setEnabled(false);
408 if(newNameForRegistrationPopupEditor == null){
409 TaxonNamePopupEditor popup = openPopupEditor(TaxonNamePopupEditor.class, event);
410 newNameForRegistrationPopupEditor = popup;
411 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
412 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE,CRUD.DELETE));
413 popup.withDeleteButton(true);
414 popup.setCdmEntityInstantiator(new BeanInstantiator<TaxonName>() {
415
416 @Override
417 public TaxonName createNewBean() {
418 TaxonName newTaxonName = TaxonNameFactory.NewNameInstance(RegistrationUIDefaults.NOMENCLATURAL_CODE, Rank.SPECIES());
419 newTaxonName.setNomenclaturalReference(getRepo().getReferenceService().load(workingset.getCitationUuid(), TaxonNameEditorPresenter.REFERENCE_INIT_STRATEGY ));
420 return newTaxonName;
421 }
422 });
423 TaxonNamePopupEditorConfig.configureForNomenclaturalAct(popup);
424 popup.loadInEditor(null);
425 }
426 }
427
428 /**
429 * Creates a new <code>Registration</code> for a new name that has just been edited
430 * using a <code>TaxonNamePopupEditor</code>. The popup editor which has been opened to
431 * edit the new name was remembered in <code>newNameForRegistrationPopupEditor</code>.
432 * Any blocking registrations which have been created while editing the new name are
433 * temporarily stored in <code>newNameBlockingRegistrations</code> until the registration
434 * for the first name has been created. Additional new names are created for example
435 * when a new name as basionym, replaced synonym, etc to the new name is created.
436 * <p>
437 * See also {@link #onTaxonNameEditorActionAdd(TaxonNameEditorAction)}).
438 *
439 * @param event
440 * @throws TypeDesignationSetException
441 * passes on the Exception which may come from onRegistrationWorkflowEventActionStart()
442 */
443 @EventBusListenerMethod
444 public void onDoneWithTaxonnameEditor(DoneWithPopupEvent event) throws TypeDesignationSetException {
445
446 if(!isFromOwnView(event)){
447 return;
448 }
449
450 if(event.getPopup() instanceof TaxonNamePopupEditor){
451
452 EditorActionContext rootContext = editorActionContextRoot(event.getPopup());
453 boolean isAddExistingNameRegistration = rootContext.getTargetField() != null && rootContext.getTargetField().equals(getView().getExistingNameCombobox());
454
455 if(isAddExistingNameRegistration){
456 if(event.getReason().equals(Reason.SAVE)){
457 onRegistrationWorkflowEventActionStart(new RegistrationWorkingsetAction(workingset.getCitationUuid(),
458 RegistrationWorkingsetAction.Action.start));
459 }
460 // just ignore on CANCEL
461 } else {
462 Optional<Registration> registrationOpt = Optional.ofNullable(null);
463 if(newNameForRegistrationPopupEditor != null && event.getPopup().equals(newNameForRegistrationPopupEditor)){
464 if(event.getReason().equals(Reason.SAVE)){
465 try {
466 TaxonName taxonName = newNameForRegistrationPopupEditor.getBean().cdmEntity();
467 registrationOpt = Optional.of(registrationWorkflowService.createRegistration(taxonName, newNameBlockingRegistrations));
468 loadWorkingSet(workingset.getCitationUuid());
469 } finally {
470 clearSession();
471 getView().getAddNewNameRegistrationButton().setEnabled(true);
472 }
473 }
474 // nullify and clear the memory on this popup editor in any case (SAVE, DELETE, CANCEL)
475 newNameForRegistrationPopupEditor = null;
476 newNameBlockingRegistrations.clear();
477 getView().getAddNewNameRegistrationButton().setEnabled(true);
478 }
479
480 if(event.getReason().equals(Reason.SAVE)){
481 if(!registrationOpt.isPresent()){
482 // no new registration has been created above, so there must be an existing one.
483 registrationOpt = findRegistrationInContext(event.getPopup());
484 }
485
486 // Check if the other names used in the context of the name are registered yet.
487 TaxonNamePopupEditor nameEditor = (TaxonNamePopupEditor)event.getPopup();
488 Set<TaxonName> namesToCheck = new HashSet<>();
489
490 namesToCheck.addAll(nameEditor.getBasionymComboboxSelect().getValue());
491 namesToCheck.addAll(nameEditor.getReplacedSynonymsComboboxSelect().getValue());
492 namesToCheck.add(nameEditor.getValidationField().getRelatedNameComboBox().getValue());
493 namesToCheck.add(nameEditor.getOrthographicVariantField().getRelatedNameComboBox().getValue());
494 // NOTE: according to https://dev.e-taxonomy.eu/redmine/issues/8049#note-2 we will not create blocking
495 // registrations for names in WeaklyRelatedEntityFields
496
497 for(TaxonName name : namesToCheck){
498 if(name != null){
499 clearSession();
500 assocciateOrQueueBlockingRegistration(registrationOpt, name.getUuid());
501 }
502 }
503 } else if (event.getReason().equals(Reason.DELETE)){
504 //FIXME handle delete: need to remove blocking registrations?
505 }
506 // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
507 refreshView(isAtContextRoot(event.getPopup()));
508 }
509 }
510 }
511
512 /**
513 * Creates a new Registration for an exiting (previously published) name.
514 */
515 @EventBusListenerMethod
516 public void onRegistrationWorkflowEventActionStart(RegistrationWorkingsetAction event) throws TypeDesignationSetException {
517
518 if(!event.isStart()){
519 return;
520 }
521
522 getView().getExistingNameCombobox().commit(); // update the chosen value in the datasource
523 TaxonName typifiedName = getView().getExistingNameCombobox().getValue();
524 if(typifiedName != null){
525 boolean doReloadWorkingSet = false;
526 try {
527 doReloadWorkingSet = registrationWorkflowService.createRegistrationforExistingName(workingset, typifiedName);
528 } finally {
529 clearSession();
530 refreshView(doReloadWorkingSet);
531 getView().getAddExistingNameRegistrationButton().setEnabled(false);
532 }
533 } else {
534 logger.error("Seletced name is NULL");
535 }
536
537 }
538
539 @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
540 public void onTypeDesignationsEditorActionEdit(TypeDesignationSetEditorAction event) {
541
542 if(!checkFromOwnView(event)){
543 return;
544 }
545
546 RegistrationDTO registrationDTO = workingset.getRegistrationDTO(event.getRegistrationUuid()).get();
547
548 if(event.getWorkingSetType() == TypeDesignationSetType.SPECIMEN_TYPE_DESIGNATION_SET ){
549 SpecimenTypeDesignationSetPopupEditor popup = openPopupEditor(SpecimenTypeDesignationSetPopupEditor.class, event);
550 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
551 popup.withDeleteButton(true);
552 popup.loadInEditor(new SpecimenTypeDesignationSetIds(
553 workingset.getCitationUuid(),
554 event.getRegistrationUuid(),
555 CdmBase.deproxy(event.getBaseEntity(), FieldUnit.class), null));
556 if(event.hasSource()){
557 // propagate readonly state from source button to popup
558 popup.setReadOnly(event.getSource().isReadOnly());
559 }
560 } else {
561 NameTypeDesignationPopupEditor popup = openPopupEditor(NameTypeDesignationPopupEditor.class, event);
562 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
563 popup.withDeleteButton(true);
564 popup.loadInEditor(NameTypeDesignationSetIds.forExistingTypeDesignation(
565 registrationDTO.getCitationUuid(),
566 CdmBase.deproxy(event.getBaseEntity(), NameTypeDesignation.class))
567 );
568 popup.getTypifiedNamesComboboxSelect().setEnabled(false);
569 if(event.hasSource()){
570 // propagate readonly state from source button to popup
571 popup.setReadOnly(event.getSource().isReadOnly());
572 }
573 nameTypeDesignationPopupEditorRegistrationUUIDMap.put(popup, event.getRegistrationUuid());
574 }
575 }
576
577 @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
578 public void onTypeDesignationSetAdd(TypeDesignationSetEditorAction event) {
579
580 if(!event.hasSource()){
581 return;
582 }
583
584 RegistrationDTO registrationDTO = workingset.getRegistrationDTO(event.getRegistrationUuid()).get();
585
586 if(event.getWorkingSetType() == TypeDesignationSetType.SPECIMEN_TYPE_DESIGNATION_SET){
587 SpecimenTypeDesignationSetPopupEditor popup = openPopupEditor(SpecimenTypeDesignationSetPopupEditor.class, event);
588 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
589 TypedEntityReference<TaxonName> typifiedNameRef;
590 if(registrationDTO.getTypifiedNameRef() != null){
591 // case for registrations without name, in which case the typifiedName is only defined via the typedesignations
592 typifiedNameRef = new TypedEntityReference(TaxonName.class, registrationDTO.getTypifiedNameRef().getUuid());
593 } else {
594 // case of registrations with a name in the nomenclatural act.
595 typifiedNameRef = new TypedEntityReference(TaxonName.class, registrationDTO.getNameRef().getUuid());
596 }
597
598 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE, CRUD.DELETE));
599 popup.withDeleteButton(false);
600 popup.loadInEditor(new SpecimenTypeDesignationSetIds(
601 workingset.getCitationUuid(),
602 event.getRegistrationUuid(),
603 null,
604 typifiedNameRef.getUuid()
605 )
606 );
607 if(event.hasSource()){
608 // propagate readonly state from source component to popup
609 popup.setReadOnly(event.getSource().isReadOnly());
610 }
611 } else {
612 NameTypeDesignationPopupEditor popup = openPopupEditor(NameTypeDesignationPopupEditor.class, event);
613 popup.setParentEditorActionContext(event.getContext(), event.getTarget());
614 popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE, CRUD.DELETE));
615 nameTypeDesignationPopupEditorRegistrationUUIDMap.put(popup, event.getRegistrationUuid());
616 popup.setBeanInstantiator(new BeanInstantiator<NameTypeDesignation>() {
617
618 @Override
619 public NameTypeDesignation createNewBean() {
620
621 TaxonName typifiedName = getRepo().getNameService().load(event.getTypifiedNameUuid(), Arrays.asList(new String[]{
622 "typeDesignations",
623 "homotypicalGroup",
624 "nomenclaturalSource.citation"
625 }));
626 NameTypeDesignation nameTypeDesignation = NameTypeDesignation.NewInstance();
627 nameTypeDesignation.getTypifiedNames().add(typifiedName);
628 return nameTypeDesignation;
629 }
630 });
631 popup.withDeleteButton(false);
632 popup.loadInEditor(NameTypeDesignationSetIds.forNewTypeDesignation(
633 registrationDTO.getCitationUuid(),
634 event.getTypifiedNameUuid()
635 )
636 );
637 popup.getTypifiedNamesComboboxSelect().setEnabled(false);
638 if(event.hasSource()){
639 // propagate readonly state from source component to popup
640 popup.setReadOnly(event.getSource().isReadOnly());
641 }
642 }
643 }
644
645 /**
646 * Performs final actions after a TypeDesignationEditor which has been
647 * opened to add a TypeDesignation to a Registration object which was
648 * created for an previously published name. Prior adding a typedesignation,
649 * the according Registration object is dangling, that has no association to
650 * any entity denoting an nomenclatural act which has a reference to a
651 * publication. This means that the registration object is not in the
652 * working set.
653 *
654 *
655 * @param event
656 * @throws TypeDesignationSetException
657 */
658 @EventBusListenerMethod
659 public void onDoneWithTypeDesignationEditor(DoneWithPopupEvent event) {
660
661 if(!isFromOwnView(event)){
662 return;
663 }
664
665 if(event.getPopup() instanceof SpecimenTypeDesignationSetPopupEditor){
666 if(event.getReason().equals(Reason.SAVE)){
667 // NOTE: adding the SpecimenTypeDesignations to the registration is done in the
668 // SpecimenTypeDesignationSetServiceImpl.save(SpecimenTypeDesignationSetDTO dto) method
669 }
670 // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
671 refreshView(isAtContextRoot(event.getPopup()));
672 } else if(event.getPopup() instanceof NameTypeDesignationPopupEditor){
673 if(event.getReason().equals(Reason.SAVE)){
674
675 Optional<Registration> registrationOpt = Optional.ofNullable(null);
676 UUID typeDesignationUuid = ((NameTypeDesignationPopupEditor)event.getPopup()).getBean().getUuid();
677 try {
678 clearSession();
679 registrationOpt = findRegistrationInContext(event.getPopup());
680 registrationOpt.ifPresent(reg -> {
681 registrationWorkflowService.addTypeDesignation(typeDesignationUuid, reg);
682 nameTypeDesignationPopupEditorRegistrationUUIDMap.remove(event.getPopup());
683 });
684
685 } finally {
686 clearSession();
687 }
688
689 // Check if other names used in the context of the name are registered yet.
690 NameTypeDesignationPopupEditor nameTypeDesignationEditor = (NameTypeDesignationPopupEditor)event.getPopup();
691 Set<TaxonName> namesToCheck = new HashSet<>();
692
693 namesToCheck.add(nameTypeDesignationEditor.getTypeNameField().getValue());
694
695 for(TaxonName name : namesToCheck){
696 if(name != null){
697 assocciateOrQueueBlockingRegistration(registrationOpt, name.getUuid());
698 }
699
700 }
701
702 } else if(event.getReason().equals(Reason.CANCEL)){
703 // noting to do
704 }
705 // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
706 refreshView(isAtContextRoot(event.getPopup()));
707
708 }
709 // ignore other editors
710 }
711
712 private void assocciateOrQueueBlockingRegistration(Optional<Registration> registrationOpt, UUID nameUuid) {
713 registrationOpt.ifPresent(reg -> registrationWorkflowService.addBlockingRegistration(nameUuid, reg));
714 if(!registrationOpt.isPresent()){
715 // not present!
716 Registration blockingRegistration = registrationWorkflowService.prepareBlockingRegistration(nameUuid);
717 if(blockingRegistration != null){
718 newNameBlockingRegistrations.add(blockingRegistration);
719 logger.debug("Blocking registration created and queued for later association with the main registration.");
720 }
721 }
722 }
723
724
725 public void clearSession() {
726 getRepo().clearSession();
727 }
728
729 @EventBusListenerMethod(filter = ShowDetailsEventEntityTypeFilter.RegistrationWorkingSet.class)
730 public void onShowDetailsEventForRegistrationWorkingSet(ShowDetailsEvent<RegistrationWorkingSet,?> event) {
731
732 if(event.getProperty().equals(RegistrationItem.VALIDATION_PROBLEMS)){
733 List<String> messages = new ArrayList<>();
734 for(RegistrationDTO dto : workingset.getRegistrationDTOs()){
735 dto.getValidationProblems().forEach(m -> messages.add(dto.getSummary() + ": " + m));
736 }
737 getView().openDetailsPopup("Validation Problems", messages);
738 }
739 }
740
741 @EventBusListenerMethod
742 public void onEntityChangeEvent(EntityChangeEvent event){
743
744 if(!isFromOwnView(event)){
745 return;
746 }
747
748 if(workingset == null){
749 return;
750 }
751 if(Reference.class.isAssignableFrom(event.getEntityType())){
752
753 if(workingset.getCitationUuid().equals(event.getEntityUuid())){
754 if(event.isRemovedType()){
755 viewEventBus.publish(EventScope.UI, this, new NavigationEvent(StartRegistrationViewBean.NAME));
756 } else {
757 refreshView(true);
758 }
759 }
760
761 } else
762 if(Registration.class.isAssignableFrom(event.getEntityType())){
763 if(workingset.getRegistrations().stream().anyMatch(reg -> reg.getUuid() == event.getEntityUuid())){
764 refreshView(true);
765 }
766 } else
767 if(TaxonName.class.isAssignableFrom(event.getEntityType())){
768 if(event.getType().equals(EntityChangeEvent.Type.CREATED)){
769 Stack<EditorActionContext>context = ((AbstractPopupEditor)event.getSourceView()).getEditorActionContext();
770 EditorActionContext rootContext = context.get(0);
771 if(rootContext.getParentView().equals(getView()) && event.getSourceView() != newNameForRegistrationPopupEditor){
772 try {
773 clearSession();
774 // create a blocking registration
775 UUID taxonNameUUID = event.getEntityUuid();
776 Optional<Registration> registrationOpt = findRegistrationInContext(context);
777 assocciateOrQueueBlockingRegistration(registrationOpt, taxonNameUUID);
778 } finally {
779 clearSession();
780 }
781
782 } else {
783 // in case of creating a new name for a registration the parent view is the TaxonNamePopupEditor
784 // this is set
785 logger.debug("Non blocking registration, since a new name for a new registration has been created");
786 }
787 }
788 if(workingset.getRegistrationDTOs().stream().anyMatch(reg ->
789 reg.getTypifiedNameRef() != null
790 && reg.getTypifiedNameRef().getUuid().equals(event.getEntityUuid()))){
791 //refreshView(true);
792 }
793 } else
794 if(TypeDesignationBase.class.isAssignableFrom(event.getEntityType())){
795 if(workingset.getRegistrationDTOs().stream().anyMatch(
796 reg -> reg.typeDesignations() != null && reg.typeDesignations().stream().anyMatch(
797 td -> td.getUuid() == event.getEntityUuid()
798 )
799 )
800 ){
801 //refreshView(true);
802 }
803 }
804 }
805
806 public Optional<Registration> findRegistrationInContext(PopupView popupView) {
807 Stack<EditorActionContext>context = ((AbstractPopupEditor)popupView).getEditorActionContext();
808 return findRegistrationInContext(context);
809 }
810
811 /**
812 * Finds the Registration in the EditorContext stack
813 */
814 public Optional<Registration> findRegistrationInContext(Stack<EditorActionContext> context) {
815 EditorActionContext rootCtx = context.get(0);
816 TypedEntityReference<Registration> regReference = (TypedEntityReference<Registration>)rootCtx.getParentEntity();
817 Optional<RegistrationDTO> registrationDTOOptional = workingset.getRegistrationDTO(regReference.getUuid());
818 Optional<Registration> registrationOptional;
819 if(!registrationDTOOptional.isPresent()){
820 logger.debug("No RegistrationDTO in found rootCtx -> user is about to create a registration for a new name.");
821 registrationOptional = Optional.ofNullable(null);
822 }
823
824 Optional<Registration> regOpt;
825 if(registrationDTOOptional.isPresent()){
826 regOpt = Optional.of(registrationDTOOptional.get().registration());
827 } else {
828 regOpt = Optional.ofNullable(null);
829 }
830
831 return regOpt;
832 }
833
834 @EventBusListenerMethod(filter = ShowDetailsEventEntityTypeFilter.RegistrationDTO.class)
835 public void onShowDetailsEventForRegistrationDTO(ShowDetailsEvent<RegistrationDTO, UUID> event) {
836
837 // FIXME check from own view!!!
838 if(getView() == null){
839 return;
840 }
841
842 UUID registrationUuid = event.getIdentifier();
843
844 RegistrationDTO regDto = workingset.getRegistrationDTO(registrationUuid).get();
845 if(event.getProperty().equals(RegistrationItem.BLOCKED_BY)){
846
847 Set<RegistrationDTO> blockingRegs;
848 if(regDto.registration().isPersisted()){
849 blockingRegs = getWorkingSetService().loadBlockingRegistrations(registrationUuid);
850 } else {
851 blockingRegs = new HashSet<RegistrationDTO>(getWorkingSetService().makeDTOs(regDto.registration().getBlockedBy()));
852 }
853 getView().setBlockingRegistrations(registrationUuid, blockingRegs);
854 } else if(event.getProperty().equals(RegistrationItem.VALIDATION_PROBLEMS)){
855 getView().openDetailsPopup("Validation Problems", regDto.getValidationProblems());
856 }
857 }
858
859 @Override
860 public ICdmEntityUuidCacher getCache() {
861 return cache;
862 }
863
864 @Override
865 public void addRootEntity(CdmBase entity) {
866 rootEntities.add(entity);
867 cache.load(entity);
868 }
869
870 @Override
871 public Collection<CdmBase> getRootEntities() {
872 return rootEntities;
873 }
874
875 @Override
876 public void destroy() throws Exception {
877 super.destroy();
878 disposeCache();
879 }
880
881 @Override
882 public void disposeCache() {
883 cache.dispose();
884 }
885
886 public boolean canCreateNameRegistrationFor(TaxonName name) {
887 return registrationWorkflowService.canCreateNameRegistrationFor(workingset, name);
888 }
889
890 public boolean checkWokingsetContainsProtologe(TaxonName name) {
891 return registrationWorkflowService.checkWokingsetContainsProtologe(workingset, name);
892 }
893 }