Project

General

Profile

Download (39.9 KB) Statistics
| Branch: | Tag: | Revision:
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.log4j.Logger;
26
import org.springframework.beans.factory.annotation.Autowired;
27
import org.vaadin.spring.events.EventScope;
28
import org.vaadin.spring.events.annotation.EventBusListenerMethod;
29

    
30
import com.vaadin.server.ClientConnector.DetachEvent;
31
import com.vaadin.server.ClientConnector.DetachListener;
32
import com.vaadin.spring.annotation.SpringComponent;
33
import com.vaadin.spring.annotation.ViewScope;
34
import com.vaadin.ui.AbstractField;
35
import com.vaadin.ui.Label;
36
import com.vaadin.ui.UI;
37
import com.vaadin.ui.VerticalLayout;
38
import com.vaadin.ui.Window;
39

    
40
import eu.etaxonomy.cdm.api.service.config.RegistrationStatusTransitions;
41
import eu.etaxonomy.cdm.api.service.dto.RegistrationDTO;
42
import eu.etaxonomy.cdm.api.service.dto.RegistrationWorkingSet;
43
import eu.etaxonomy.cdm.api.service.exception.RegistrationValidationException;
44
import eu.etaxonomy.cdm.api.service.name.TypeDesignationWorkingSet.TypeDesignationWorkingSetType;
45
import eu.etaxonomy.cdm.api.service.registration.IRegistrationWorkingSetService;
46
import eu.etaxonomy.cdm.api.utility.UserHelper;
47
import eu.etaxonomy.cdm.cache.CdmTransientEntityAndUuidCacher;
48
import eu.etaxonomy.cdm.model.ICdmEntityUuidCacher;
49
import eu.etaxonomy.cdm.model.common.CdmBase;
50
import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
51
import eu.etaxonomy.cdm.model.name.Rank;
52
import eu.etaxonomy.cdm.model.name.Registration;
53
import eu.etaxonomy.cdm.model.name.RegistrationStatus;
54
import eu.etaxonomy.cdm.model.name.TaxonName;
55
import eu.etaxonomy.cdm.model.name.TaxonNameFactory;
56
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
57
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
58
import eu.etaxonomy.cdm.model.permission.CRUD;
59
import eu.etaxonomy.cdm.model.reference.Reference;
60
import eu.etaxonomy.cdm.ref.TypedEntityReference;
61
import eu.etaxonomy.cdm.service.CdmBeanItemContainerFactory;
62
import eu.etaxonomy.cdm.service.CdmFilterablePagingProvider;
63
import eu.etaxonomy.cdm.service.CdmFilterablePagingProviderFactory;
64
import eu.etaxonomy.cdm.service.CdmStore;
65
import eu.etaxonomy.cdm.service.IRegistrationWorkflowService;
66
import eu.etaxonomy.cdm.service.UserHelperAccess;
67
import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationItem;
68
import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationStatusFieldInstantiator;
69
import eu.etaxonomy.cdm.vaadin.component.registration.RegistrationStatusSelect;
70
import eu.etaxonomy.cdm.vaadin.event.EditorActionContext;
71
import eu.etaxonomy.cdm.vaadin.event.EditorActionTypeFilter;
72
import eu.etaxonomy.cdm.vaadin.event.EntityChangeEvent;
73
import eu.etaxonomy.cdm.vaadin.event.ReferenceEditorAction;
74
import eu.etaxonomy.cdm.vaadin.event.RegistrationEditorAction;
75
import eu.etaxonomy.cdm.vaadin.event.ShowDetailsEvent;
76
import eu.etaxonomy.cdm.vaadin.event.ShowDetailsEventEntityTypeFilter;
77
import eu.etaxonomy.cdm.vaadin.event.TaxonNameEditorAction;
78
import eu.etaxonomy.cdm.vaadin.event.TypeDesignationWorkingsetEditorAction;
79
import eu.etaxonomy.cdm.vaadin.event.registration.RegistrationWorkingsetAction;
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.NameTypeDesignationWorkingsetIds;
86
import eu.etaxonomy.cdm.vaadin.view.name.SpecimenTypeDesignationWorkingsetIds;
87
import eu.etaxonomy.cdm.vaadin.view.name.SpecimenTypeDesignationWorkingsetPopupEditor;
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
 */
106
@SpringComponent
107
@ViewScope
108
public class RegistrationWorkingsetPresenter extends AbstractPresenter<RegistrationWorkingsetView> implements CachingPresenter {
109

    
110
    private static final Logger logger = Logger.getLogger(RegistrationWorkingsetPresenter.class);
111

    
112
    private static final long serialVersionUID = 1L;
113

    
114
    @Autowired
115
    private IRegistrationWorkingSetService regWorkingSetService;
116

    
117
    @Autowired
118
    private IRegistrationWorkflowService registrationWorkflowService;
119

    
120
    @Autowired
121
    private CdmFilterablePagingProviderFactory pagingProviderFactory;
122

    
123
    @Autowired
124
    private CdmBeanItemContainerFactory selectFieldFactory;
125

    
126
    @Autowired
127
    private CdmStore cdmStore;
128

    
129
    /**
130
     * @return the regWorkingSetService
131
     */
132
    public IRegistrationWorkingSetService getWorkingSetService() {
133
        return regWorkingSetService;
134
    }
135

    
136
    private RegistrationWorkingSet workingset;
137

    
138
    /**
139
     * 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.
140
     * There can always only be one popup editor for this purpose.
141
     */
142
    private TaxonNamePopupEditor newNameForRegistrationPopupEditor = null;
143

    
144
    /**
145
     * Contains
146
     */
147
    private List<Registration> newNameBlockingRegistrations = new ArrayList<>();
148

    
149
    /**
150
     * TODO is this still needed? The regitration UUID should be accessible in the popup editor context,
151
     * see findRegistrationInContext()
152
     */
153
    private Map<NameTypeDesignationPopupEditor, UUID> nameTypeDesignationPopupEditorRegistrationUUIDMap = new HashMap<>();
154

    
155

    
156
    private ICdmEntityUuidCacher cache;
157

    
158
    private Collection<CdmBase> rootEntities = new HashSet<>();
159

    
160

    
161
    public RegistrationWorkingsetPresenter() {
162
    }
163

    
164
    /**
165

    
166

    
167
    /**
168
     * @param doReload reload the workingset from the persistent storage.
169
     *  Workingsets which are not yet persisted are preserved.
170
     *
171
     */
172
    protected void refreshView(boolean doReload) {
173

    
174
        if(workingset == null){
175
            return; // nothing to do
176
        }
177
        if(doReload){
178
            if(logger.isDebugEnabled()){
179
                logger.debug("refreshView() - workingset:\n" + workingset.toString());
180
            }
181
            List<RegistrationDTO> unpersisted = new ArrayList<>();
182
            for(RegistrationDTO regDto : workingset.getRegistrationDTOs()){
183
                if(!regDto.registration().isPersited()){
184
                    unpersisted.add(regDto);
185
                }
186
            }
187
            loadWorkingSet(workingset.getCitationUuid());
188
            for(RegistrationDTO regDtoUnpersisted : unpersisted){
189
                if(!workingset.getRegistrationDTOs().stream().anyMatch(dto -> dto.getUuid().equals(regDtoUnpersisted.getUuid()))){
190
                    // only add if the regDtoUnpersisted has not been persisted meanwhile
191
                    try {
192
                        workingset.add(regDtoUnpersisted);
193
                    } catch (RegistrationValidationException e) {
194
                        // would never happen here //
195
                    }
196
                }
197
            }
198
            if(logger.isDebugEnabled()){
199
                logger.debug("refreshView() - workingset reloaded:\n" + workingset.toString());
200
            }
201
        }
202
        applyWorkingset();
203
    }
204

    
205
    /**
206
     * {@inheritDoc}
207
     */
208
    @Override
209
    public void handleViewEntered() {
210
        super.handleViewEntered();
211
        // TODO currently cannot specify type more precisely, see AbstractSelect
212
        // FIXME externalize into class file!!!!!!!!!!!!
213
        getView().setStatusComponentInstantiator(new RegistrationStatusFieldInstantiator<Object>(){
214

    
215
            private static final long serialVersionUID = 7099181280977511048L;
216

    
217
            @Override
218
            public AbstractField<Object> create(RegistrationDTO regDto) {
219

    
220
                // submitters have GrantedAuthorities like REGISTRATION(PREPARATION).[UPDATE]{ab4459eb-3b96-40ba-bfaa-36915107d59e}
221
                UserHelper userHelper = UserHelperAccess.userHelper().withCache(getCache());
222
                Set<RegistrationStatus> availableStatus = new HashSet<>();
223

    
224
                boolean canChangeStatus = userHelper.userHasPermission(regDto.registration(), CRUD.UPDATE);
225
                availableStatus.add(regDto.getStatus());
226
                if(canChangeStatus){
227
                    if(userHelper.userIsAdmin()){
228
                        availableStatus.addAll(Arrays.asList(RegistrationStatus.values()));
229
                    } else {
230
                        availableStatus.addAll(RegistrationStatusTransitions.possibleTransitions(regDto.getStatus()));
231
                    }
232
                }
233

    
234
                RegistrationStatusSelect select = new RegistrationStatusSelect(null, selectFieldFactory.buildEnumTermItemContainer(
235
                        RegistrationStatus.class,
236
                        availableStatus.toArray(new RegistrationStatus[availableStatus.size()]))
237
                        );
238
                select.setValue(regDto.getStatus());
239
                select.addValueChangeListener(e -> saveRegistrationStatusChange(regDto.getUuid(), e.getProperty().getValue()));
240
                select.setEnabled(canChangeStatus);
241
                select.setNullSelectionAllowed(false);
242
                return select;
243
            }
244

    
245

    
246
        });
247

    
248
        loadWorkingSet(getView().getCitationUuid());
249
        applyWorkingset();
250

    
251
    }
252

    
253
    private void applyWorkingset(){
254
         getView().setWorkingset(workingset);
255
        // PagingProviders and CacheGenerator for the existingNameCombobox
256
        activateComboboxes();
257
    }
258

    
259
    protected void activateComboboxes() {
260
        CdmTitleCacheCaptionGenerator<TaxonName> titleCacheGenerator = new CdmTitleCacheCaptionGenerator<TaxonName>();
261
        getView().getAddExistingNameCombobox().setCaptionGenerator(titleCacheGenerator);
262
        CdmFilterablePagingProvider<TaxonName, TaxonName> pagingProvider = pagingProviderFactory.taxonNamesWithoutOrthophicIncorrect();
263
        getView().getAddExistingNameCombobox().loadFrom(pagingProvider, pagingProvider, pagingProvider.getPageSize());
264
    }
265

    
266
    /**
267
     * @param referenceID
268
     */
269
    protected void loadWorkingSet(UUID referenceUuid) {
270

    
271
        try {
272
            workingset = getWorkingSetService().loadWorkingSetByReferenceUuid(referenceUuid, true);
273
        } catch (RegistrationValidationException error) {
274
            logger.error(error);
275
            showErrorDialog("Validation Error", error.getMessage());
276
        }
277
        cache = new CdmTransientEntityAndUuidCacher(this);
278
        for(Registration registration : workingset.getRegistrations()) {
279
            addRootEntity(registration);
280
        }
281
    }
282

    
283
    /**
284
     * @param errorDialogCaption
285
     * @param errorMessage
286
     */
287
    public void showErrorDialog(String errorDialogCaption, String errorMessage) {
288
        Window errorDialog = new Window(errorDialogCaption);
289
        errorDialog.setModal(true);
290
        VerticalLayout subContent = new VerticalLayout();
291
        subContent.setMargin(true);
292
        errorDialog.setContent(subContent);
293
        subContent.addComponent(new Label(errorMessage));
294
        UI.getCurrent().addWindow(errorDialog);
295
    }
296

    
297
    private void saveRegistrationStatusChange(UUID uuid, Object value) {
298
        Registration reg = getRepo().getRegistrationService().load(uuid);
299
        if(reg == null){
300
            // registration was not yet persisted, ignore
301
            return;
302
        }
303
        if(value != null && value instanceof RegistrationStatus){
304
            if(!Objects.equals(value, reg.getStatus())){
305
                reg.updateStatusAndDate((RegistrationStatus)value);
306
                cdmStore.saveBean(reg, (AbstractView)getView());
307
                refreshView(true);
308
            }
309
        } else {
310
            // only log here as error
311
            logger.error("Ivalid attempt to set RegistrationStatus to " + Objects.toString(value.toString(), "NULL"));
312
        }
313
    }
314

    
315

    
316
    @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
317
    public void onReferenceEditorActionAdd(ReferenceEditorAction event) {
318

    
319
        if(!checkFromOwnView(event)){
320
            return;
321
        }
322

    
323
        ReferencePopupEditor popup = openPopupEditor(ReferencePopupEditor.class, event);
324
        popup.withReferenceTypes(RegistrationUIDefaults.PRINTPUB_REFERENCE_TYPES);
325
        popup.loadInEditor(null);
326
    }
327

    
328
    @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
329
    public void onReferenceEditorActionEdit(ReferenceEditorAction event) {
330

    
331
        if(!checkFromOwnView(event)){
332
            return;
333
        }
334
        ReferencePopupEditor popup = openPopupEditor(ReferencePopupEditor.class, event);
335
        popup.withReferenceTypes(RegistrationUIDefaults.PRINTPUB_REFERENCE_TYPES);
336
        popup.withDeleteButton(true);
337
        popup.loadInEditor(event.getEntityUuid());
338
    }
339

    
340
    @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
341
    public void onRegistrationEditorAction(RegistrationEditorAction event) {
342

    
343
        if(!checkFromOwnView(event)){
344
            return;
345
        }
346

    
347
        RegistrationPopupEditor popup = openPopupEditor(RegistrationPopupEditor.class, event);
348
        popup.loadInEditor(event.getEntityUuid());
349
    }
350

    
351
    @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
352
    public void onTaxonNameEditorActionEdit(TaxonNameEditorAction event) {
353

    
354
        if(!checkFromOwnView(event)){
355
            return;
356
        }
357

    
358
        boolean isAddExistingNameRegistration = event.getTarget() != null && event.getTarget().equals(getView().getAddExistingNameCombobox());
359

    
360
        TaxonNamePopupEditor popup = openPopupEditor(TaxonNamePopupEditor.class, event);
361

    
362
        popup.setParentEditorActionContext(event.getContext(), event.getTarget());
363
        popup.withDeleteButton(!isAddExistingNameRegistration);
364
        TaxonNamePopupEditorConfig.configureForNomenclaturalAct(popup);
365
        if(isAddExistingNameRegistration){
366
            // allow saving even if the name parts are not valid
367
            // the user will need to fix this in a later step
368
            popup.disableMode(TaxonNamePopupEditorMode.VALIDATE_AGAINST_HIGHER_NAME_PART);
369
            getView().getAddExistingNameRegistrationButton().setEnabled(false);
370
            popup.addDetachListener(new DetachListener() {
371

    
372
                @Override
373
                public void detach(DetachEvent event) {
374
                    getView().getAddExistingNameRegistrationButton().setEnabled(true);
375

    
376
                }
377
            });
378
        }
379
        popup.loadInEditor(event.getEntityUuid());
380
        if(event.hasSource() && event.getSource().isReadOnly()){
381
            // avoid resetting readonly to false
382
            popup.setReadOnly(true);
383
        }
384

    
385
        boolean hasNomRef = popup.getBean().getNomenclaturalReference() != null;
386
        if(isAddExistingNameRegistration){
387
            popup.setAllFieldsReadOnly(true);
388
            if(!hasNomRef){
389
                // only allow editing the nomenclatural reference, all other
390
                // editing need to be done another way.
391
                // Otherwise we would need to be prepared for creating blocking registrations
392
                // in turn of creation, modification of related taxon names.
393
                popup.disableMode(TaxonNamePopupEditorMode.NOMENCLATURALREFERENCE_SECTION_EDITING_ONLY);
394
                popup.getNomReferenceCombobox().setReadOnly(false);
395
                popup.getNomenclaturalReferenceDetail().setReadOnly(false);
396
                popup.addStatusMessage("The chosen name needs to be completed before it can be used. "
397
                        + "Please add the nomenclatural reference and click on \"Save\" to proceed with entering the type of this name.");
398
            } else {
399
                popup.addStatusMessage("You are about to create a registration for this name. "
400
                        + "This editor is for reviewing the name only. Therefore, all fields have been switched to readonly state. "
401
                        + "Click on \"Save\" to proceed with entering the type of this name.");
402
            }
403
        }
404
    }
405

    
406

    
407
    @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
408
    public void onTaxonNameEditorActionAdd(TaxonNameEditorAction event) {
409

    
410
        if(!checkFromOwnView(event)){
411
            return;
412
        }
413

    
414
        getView().getAddNewNameRegistrationButton().setEnabled(false);
415
        if(newNameForRegistrationPopupEditor == null){
416
            TaxonNamePopupEditor popup = openPopupEditor(TaxonNamePopupEditor.class, event);
417
            newNameForRegistrationPopupEditor = popup;
418
            popup.setParentEditorActionContext(event.getContext(), event.getTarget());
419
            popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE,CRUD.DELETE));
420
            popup.withDeleteButton(true);
421
            popup.setCdmEntityInstantiator(new BeanInstantiator<TaxonName>() {
422

    
423
                @Override
424
                public TaxonName createNewBean() {
425
                    TaxonName newTaxonName = TaxonNameFactory.NewNameInstance(RegistrationUIDefaults.NOMENCLATURAL_CODE, Rank.SPECIES());
426
                    newTaxonName.setNomenclaturalReference(getRepo().getReferenceService().load(workingset.getCitationUuid(), TaxonNameEditorPresenter.REFERENCE_INIT_STRATEGY ));
427
                    return newTaxonName;
428
                }
429
            });
430
            TaxonNamePopupEditorConfig.configureForNomenclaturalAct(popup);
431
            popup.loadInEditor(null);
432
        }
433
    }
434

    
435
    /**
436
     * Creates a new <code>Registration</code> for a new name that has just been edited
437
     * using a <code>TaxonNamePopupEditor</code>. The popup editor which has been opened to
438
     * edit the new name was remembered in <code>newNameForRegistrationPopupEditor</code>.
439
     * Any blocking registrations which have been created while editing the new name are
440
     * temporarily stored in <code>newNameBlockingRegistrations</code> until the registration
441
     * for the first name has been created. Additional new names are created for example
442
     * when a new name as basionym, replaced synonym, etc to the new name is created.
443
     * <p>
444
     * See also {@link #onTaxonNameEditorActionAdd(TaxonNameEditorAction)}).
445
     *
446
     * @param event
447
     * @throws RegistrationValidationException
448
     *      passes on the Exception which may come from onRegistrationWorkflowEventActionStart()
449
     */
450
    @EventBusListenerMethod
451
    public void onDoneWithTaxonnameEditor(DoneWithPopupEvent event) throws RegistrationValidationException {
452

    
453
        if(!isFromOwnView(event)){
454
            return;
455
        }
456

    
457
        if(event.getPopup() instanceof TaxonNamePopupEditor){
458

    
459
                EditorActionContext rootContext = editorActionContextRoot(event.getPopup());
460
                boolean isAddExistingNameRegistration = rootContext.getTargetField() != null && rootContext.getTargetField().equals(getView().getAddExistingNameCombobox());
461

    
462
                if(isAddExistingNameRegistration){
463
                    if(event.getReason().equals(Reason.SAVE)){
464
                    onRegistrationWorkflowEventActionStart(new RegistrationWorkingsetAction(workingset.getCitationUuid(),
465
                            RegistrationWorkingsetAction.Action.start));
466
                    }
467
                    // just ignore on CANCEL
468
                } else {
469
                    Optional<Registration> registrationOpt = Optional.ofNullable(null);
470
                    if(newNameForRegistrationPopupEditor != null && event.getPopup().equals(newNameForRegistrationPopupEditor)){
471
                        if(event.getReason().equals(Reason.SAVE)){
472
                            try {
473
                                TaxonName taxonName = newNameForRegistrationPopupEditor.getBean().cdmEntity();
474
                                registrationOpt = Optional.of(registrationWorkflowService.createRegistration(taxonName, newNameBlockingRegistrations));
475
                                loadWorkingSet(workingset.getCitationUuid());
476
                            } finally {
477
                                clearSession();
478
                                getView().getAddNewNameRegistrationButton().setEnabled(true);
479
                            }
480
                        }
481
                        // nullify and clear the memory on this popup editor in any case (SAVE, DELETE, CANCEL)
482
                        newNameForRegistrationPopupEditor = null;
483
                        newNameBlockingRegistrations.clear();
484
                        getView().getAddNewNameRegistrationButton().setEnabled(true);
485
                    }
486

    
487
                    if(event.getReason().equals(Reason.SAVE)){
488
                        if(!registrationOpt.isPresent()){
489
                            // no new registration has been created above, so there must be an existing one.
490
                            registrationOpt = findRegistrationInContext(event.getPopup());
491
                        }
492

    
493
                        // Check if the other names used in the context of the name are registered yet.
494
                        TaxonNamePopupEditor nameEditor = (TaxonNamePopupEditor)event.getPopup();
495
                        Set<TaxonName> namesToCheck = new HashSet<>();
496

    
497
                        namesToCheck.addAll(nameEditor.getBasionymComboboxSelect().getValue());
498
                        namesToCheck.addAll(nameEditor.getReplacedSynonymsComboboxSelect().getValue());
499
                        namesToCheck.add(nameEditor.getValidationField().getRelatedNameComboBox().getValue());
500
                        namesToCheck.add(nameEditor.getOrthographicVariantField().getRelatedNameComboBox().getValue());
501
                        // NOTE: according to https://dev.e-taxonomy.eu/redmine/issues/8049#note-2 we will not create blocking
502
                        // registrations for names in WeaklyRelatedEntityFields
503

    
504
                        for(TaxonName name : namesToCheck){
505
                            if(name != null){
506
                                clearSession();
507
                                assocciateOrQueueBlockingRegistration(registrationOpt, name.getUuid());
508
                            }
509
                        }
510
                    } else if (event.getReason().equals(Reason.DELETE)){
511
                        //FIXME handle delete: need to remove blocking registrations?
512
                    }
513
                    // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
514
                    refreshView(isAtContextRoot(event.getPopup()));
515
                }
516
        }
517
    }
518

    
519

    
520
    /**
521
     * Creates a new Registration for an exiting (previously published) name.
522
     *
523
     * @param event
524
     * @throws RegistrationValidationException
525
     */
526
    @EventBusListenerMethod
527
    public void onRegistrationWorkflowEventActionStart(RegistrationWorkingsetAction event) throws RegistrationValidationException {
528

    
529
        if(!event.isStart()){
530
            return;
531
        }
532

    
533
        getView().getAddExistingNameCombobox().commit(); // update the chosen value in the datasource
534
        TaxonName typifiedName = getView().getAddExistingNameCombobox().getValue();
535
        if(typifiedName != null){
536
            boolean doReloadWorkingSet = false;
537
            try {
538
                doReloadWorkingSet = registrationWorkflowService.createRegistrationforExistingName(workingset, typifiedName);
539
            } finally {
540
                clearSession();
541
                refreshView(doReloadWorkingSet);
542
                getView().getAddExistingNameRegistrationButton().setEnabled(false);
543
            }
544
        } else {
545
            logger.error("Seletced name is NULL");
546
        }
547

    
548
    }
549

    
550
    @EventBusListenerMethod(filter = EditorActionTypeFilter.Edit.class)
551
    public void onTypeDesignationsEditorActionEdit(TypeDesignationWorkingsetEditorAction event) {
552

    
553
        if(!checkFromOwnView(event)){
554
            return;
555
        }
556

    
557
        if(event.getWorkingSetType() == TypeDesignationWorkingSetType.SPECIMEN_TYPE_DESIGNATION_WORKINGSET ){
558
            SpecimenTypeDesignationWorkingsetPopupEditor popup = openPopupEditor(SpecimenTypeDesignationWorkingsetPopupEditor.class, event);
559
            popup.setParentEditorActionContext(event.getContext(), event.getTarget());
560
            popup.withDeleteButton(true);
561
            popup.loadInEditor(new SpecimenTypeDesignationWorkingsetIds(
562
                    event.getRegistrationUuid(),
563
                    event.getBaseEntityRef().castTo(FieldUnit.class), null));
564
            if(event.hasSource()){
565
                // propagate readonly state from source button to popup
566
                popup.setReadOnly(event.getSource().isReadOnly());
567
            }
568
        } else {
569
            NameTypeDesignationPopupEditor popup = openPopupEditor(NameTypeDesignationPopupEditor.class, event);
570
            popup.setParentEditorActionContext(event.getContext(), event.getTarget());
571
            popup.withDeleteButton(true);
572
            popup.loadInEditor(new NameTypeDesignationWorkingsetIds(
573
                    event.getRegistrationUuid(),
574
                    event.getBaseEntityRef().castTo(NameTypeDesignation.class))
575
                    );
576
            popup.getTypifiedNamesComboboxSelect().setEnabled(false);
577
            if(event.hasSource()){
578
                // propagate readonly state from source button to popup
579
                popup.setReadOnly(event.getSource().isReadOnly());
580
            }
581
            nameTypeDesignationPopupEditorRegistrationUUIDMap.put(popup, event.getRegistrationUuid());
582
        }
583
    }
584

    
585
    @EventBusListenerMethod(filter = EditorActionTypeFilter.Add.class)
586
    public void onTypeDesignationWorkingsetAdd(TypeDesignationWorkingsetEditorAction event) {
587

    
588
        if(!event.hasSource()){
589
            return;
590
        }
591

    
592
        if(event.getWorkingSetType() == TypeDesignationWorkingSetType.SPECIMEN_TYPE_DESIGNATION_WORKINGSET){
593
            SpecimenTypeDesignationWorkingsetPopupEditor popup = openPopupEditor(SpecimenTypeDesignationWorkingsetPopupEditor.class, event);
594
            popup.setParentEditorActionContext(event.getContext(), event.getTarget());
595
            UUID typifiedNameUuid;
596

    
597
            RegistrationDTO registrationDTO = workingset.getRegistrationDTO(event.getRegistrationUuid()).get();
598
            TypedEntityReference<TaxonName> typifiedNameRef;
599
            if(registrationDTO.getTypifiedNameRef() != null){
600
                // case for registrations without name, in which case the typifiedName is only defined via the typedesignations
601
                typifiedNameRef = new TypedEntityReference(TaxonName.class, registrationDTO.getTypifiedNameRef().getUuid());
602
            } else {
603
                // case of registrations with a name in the nomenclatural act.
604
                typifiedNameRef = new TypedEntityReference(TaxonName.class, registrationDTO.getNameRef().getUuid());
605
            }
606

    
607
            popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE, CRUD.DELETE));
608
            popup.withDeleteButton(false);
609
            popup.loadInEditor(new SpecimenTypeDesignationWorkingsetIds(
610
                        event.getRegistrationUuid(),
611
                        null,
612
                        typifiedNameRef.getUuid()
613
                        )
614
                    );
615
            if(event.hasSource()){
616
                // propagate readonly state from source component to popup
617
                popup.setReadOnly(event.getSource().isReadOnly());
618
            }
619
        } else {
620
            NameTypeDesignationPopupEditor popup = openPopupEditor(NameTypeDesignationPopupEditor.class, event);
621
            popup.setParentEditorActionContext(event.getContext(), event.getTarget());
622
            popup.grantToCurrentUser(EnumSet.of(CRUD.UPDATE, CRUD.DELETE));
623
            nameTypeDesignationPopupEditorRegistrationUUIDMap.put(popup, event.getRegistrationUuid());
624
            popup.setBeanInstantiator(new BeanInstantiator<NameTypeDesignation>() {
625

    
626
                @Override
627
                public NameTypeDesignation createNewBean() {
628

    
629
                    TaxonName typifiedName = getRepo().getNameService().load(event.getTypifiedNameUuid(), Arrays.asList(new String[]{
630
                            "typeDesignations",
631
                            "homotypicalGroup",
632
                            "nomenclaturalSource.citation"
633
                            }));
634
                    NameTypeDesignation nameTypeDesignation  = NameTypeDesignation.NewInstance();
635
                    nameTypeDesignation.getTypifiedNames().add(typifiedName);
636
                    return nameTypeDesignation;
637
                }
638
            });
639
            popup.withDeleteButton(false);
640
            popup.loadInEditor(new NameTypeDesignationWorkingsetIds(
641
                    event.getRegistrationUuid(),
642
                    event.getTypifiedNameUuid()
643
                    )
644
                );
645
            popup.getTypifiedNamesComboboxSelect().setEnabled(false);
646
            if(event.hasSource()){
647
                // propagate readonly state from source component to popup
648
                popup.setReadOnly(event.getSource().isReadOnly());
649
            }
650
        }
651
    }
652

    
653
    /**
654
     * Performs final actions after a TypeDesignationEditor which has been
655
     * opened to add a TypeDesignation to a Registration object which was
656
     * created for an previously published name. Prior adding a typedesignation,
657
     * the according Registration object is dangling, that has no association to
658
     * any entity denoting an nomenclatural act which has a reference to a
659
     * publication. This means that the registration object is not in the
660
     * working set.
661
     *
662
     *
663
     * @param event
664
     * @throws RegistrationValidationException
665
     */
666
    @EventBusListenerMethod
667
    public void onDoneWithTypeDesignationEditor(DoneWithPopupEvent event) {
668

    
669
        if(!isFromOwnView(event)){
670
            return;
671
        }
672

    
673
        if(event.getPopup() instanceof SpecimenTypeDesignationWorkingsetPopupEditor){
674
            if(event.getReason().equals(Reason.SAVE)){
675
                // NOTE: adding the SpecimenTypeDesignations to the registration is done in the
676
                // SpecimenTypeDesignationWorkingSetServiceImpl.save(SpecimenTypeDesignationWorkingSetDTO dto) method
677
            }
678
            // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
679
            refreshView(isAtContextRoot(event.getPopup()));
680
        } else if(event.getPopup() instanceof NameTypeDesignationPopupEditor){
681
            if(event.getReason().equals(Reason.SAVE)){
682

    
683
                Optional<Registration> registrationOpt = Optional.ofNullable(null);
684

    
685
                UUID typeDesignationUuid = ((NameTypeDesignationPopupEditor)event.getPopup()).getBean().getUuid();
686

    
687
                try {
688
                    clearSession();
689
                    registrationOpt = findRegistrationInContext(event.getPopup());
690
                    registrationOpt.ifPresent(reg -> {
691
                        registrationWorkflowService.addTypeDesignation(typeDesignationUuid, reg);
692
                        nameTypeDesignationPopupEditorRegistrationUUIDMap.remove(event.getPopup());
693
                        });
694

    
695
                } finally {
696
                    clearSession();
697
                }
698

    
699
                // Check if other names used in the context of the name are registered yet.
700
                NameTypeDesignationPopupEditor nameTypeDesignationEditor = (NameTypeDesignationPopupEditor)event.getPopup();
701
                Set<TaxonName> namesToCheck = new HashSet<>();
702

    
703
                namesToCheck.add(nameTypeDesignationEditor.getTypeNameField().getValue());
704

    
705
                for(TaxonName name : namesToCheck){
706
                    if(name != null){
707
                        assocciateOrQueueBlockingRegistration(registrationOpt, name.getUuid());
708
                    }
709

    
710
                }
711

    
712
            } else if(event.getReason().equals(Reason.CANCEL)){
713
                // noting to do
714
            }
715
            // always reload if the first editor is closed as the data might have been changed through any other sub-popupeditor
716
            refreshView(isAtContextRoot(event.getPopup()));
717

    
718
        }
719
        // ignore other editors
720
    }
721

    
722
    /**
723
     * @param registrationOpt
724
     * @param name
725
     */
726
    private void assocciateOrQueueBlockingRegistration(Optional<Registration> registrationOpt, UUID nameUuid) {
727
        registrationOpt.ifPresent(reg -> registrationWorkflowService.addBlockingRegistration(nameUuid, reg));
728
        if(!registrationOpt.isPresent()){
729
            // not present!
730
            Registration blockingRegistration = registrationWorkflowService.prepareBlockingRegistration(nameUuid);
731
            if(blockingRegistration != null){
732
                newNameBlockingRegistrations.add(blockingRegistration);
733
                logger.debug("Blocking registration created and queued for later association with the main registration.");
734
            }
735
        }
736
    }
737

    
738
    /**
739
     *
740
     */
741
    public void clearSession() {
742
        getRepo().clearSession();
743
    }
744

    
745

    
746
    @EventBusListenerMethod(filter = ShowDetailsEventEntityTypeFilter.RegistrationWorkingSet.class)
747
    public void onShowDetailsEventForRegistrationWorkingSet(ShowDetailsEvent<RegistrationWorkingSet,?> event) {
748

    
749
        if(event.getProperty().equals(RegistrationItem.VALIDATION_PROBLEMS)){
750
            List<String> messages = new ArrayList<>();
751
            for(RegistrationDTO dto : workingset.getRegistrationDTOs()){
752
                dto.getValidationProblems().forEach(m -> messages.add(dto.getSummary() + ": " + m));
753
            }
754
            getView().openDetailsPopup("Validation Problems", messages);
755
        }
756
    }
757

    
758
    @EventBusListenerMethod
759
    public void onEntityChangeEvent(EntityChangeEvent event){
760

    
761
        if(!isFromOwnView(event)){
762
            return;
763
        }
764

    
765
        if(workingset == null){
766
            return;
767
        }
768
        if(Reference.class.isAssignableFrom(event.getEntityType())){
769

    
770
            if(workingset.getCitationUuid().equals(event.getEntityUuid())){
771
                if(event.isRemovedType()){
772
                    viewEventBus.publish(EventScope.UI, this, new NavigationEvent(StartRegistrationViewBean.NAME));
773
                } else {
774
                    refreshView(true);
775
                }
776
            }
777

    
778
        } else
779
        if(Registration.class.isAssignableFrom(event.getEntityType())){
780
            if(workingset.getRegistrations().stream().anyMatch(reg -> reg.getUuid() == event.getEntityUuid())){
781
                refreshView(true);
782
            }
783
        } else
784
        if(TaxonName.class.isAssignableFrom(event.getEntityType())){
785
            if(event.getType().equals(EntityChangeEvent.Type.CREATED)){
786
                Stack<EditorActionContext>context = ((AbstractPopupEditor)event.getSourceView()).getEditorActionContext();
787
                EditorActionContext rootContext = context.get(0);
788
                if(rootContext.getParentView().equals(getView()) && event.getSourceView() != newNameForRegistrationPopupEditor){
789
                    try {
790
                        clearSession();
791
                        // create a blocking registration
792
                        UUID taxonNameUUID = event.getEntityUuid();
793
                        Optional<Registration> registrationOpt = findRegistrationInContext(context);
794
                        assocciateOrQueueBlockingRegistration(registrationOpt, taxonNameUUID);
795
                    } finally {
796
                        clearSession();
797
                    }
798

    
799
                } else {
800
                    // in case of creating a new name for a registration the parent view is the TaxonNamePopupEditor
801
                    // this is set
802
                    logger.debug("Non blocking registration, since a new name for a new registration has been created");
803
                }
804
            }
805
            if(workingset.getRegistrationDTOs().stream().anyMatch(reg ->
806
                reg.getTypifiedNameRef() != null
807
                && reg.getTypifiedNameRef().getUuid().equals(event.getEntityUuid()))){
808
                    //refreshView(true);
809
            }
810
        } else
811
        if(TypeDesignationBase.class.isAssignableFrom(event.getEntityType())){
812
            if(workingset.getRegistrationDTOs().stream().anyMatch(
813
                    reg -> reg.typeDesignations() != null && reg.typeDesignations().stream().anyMatch(
814
                            td -> td.getUuid() == event.getEntityUuid()
815
                            )
816
                        )
817
                    ){
818
                //refreshView(true);
819
            }
820
        }
821
    }
822

    
823

    
824

    
825
    public Optional<Registration> findRegistrationInContext(PopupView popupView) {
826
        Stack<EditorActionContext>context = ((AbstractPopupEditor)popupView).getEditorActionContext();
827
        return findRegistrationInContext(context);
828
    }
829

    
830
    /**
831
     * Finds the Registration in the EditorContext stack
832
     *
833
     * @param context
834
     * @return
835
     */
836
    public Optional<Registration> findRegistrationInContext(Stack<EditorActionContext> context) {
837
        EditorActionContext rootCtx = context.get(0);
838
        TypedEntityReference<Registration> regReference = (TypedEntityReference<Registration>)rootCtx.getParentEntity();
839
        Optional<RegistrationDTO> registrationDTOOptional = workingset.getRegistrationDTO(regReference.getUuid());
840
        Optional<Registration> registrationOptional;
841
        if(!registrationDTOOptional.isPresent()){
842
            logger.debug("No RegistrationDTO in found rootCtx -> user is about to create a registration for a new name.");
843
            registrationOptional = Optional.ofNullable(null);
844
        }
845

    
846
        Optional<Registration> regOpt;
847
        if(registrationDTOOptional.isPresent()){
848
            regOpt = Optional.of(registrationDTOOptional.get().registration());
849
        } else {
850
            regOpt = Optional.ofNullable(null);
851
        }
852

    
853
        return regOpt;
854
    }
855

    
856
    @EventBusListenerMethod(filter = ShowDetailsEventEntityTypeFilter.RegistrationDTO.class)
857
    public void onShowDetailsEventForRegistrationDTO(ShowDetailsEvent<RegistrationDTO, UUID> event) {
858

    
859
        // FIXME check from own view!!!
860
        if(getView() == null){
861
            return;
862
        }
863

    
864
        UUID registrationUuid = event.getIdentifier();
865

    
866
        RegistrationDTO regDto = workingset.getRegistrationDTO(registrationUuid).get();
867
        if(event.getProperty().equals(RegistrationItem.BLOCKED_BY)){
868

    
869
            Set<RegistrationDTO> blockingRegs;
870
            if(regDto.registration().isPersited()){
871
                blockingRegs = getWorkingSetService().loadBlockingRegistrations(registrationUuid);
872
            } else {
873
                blockingRegs = new HashSet<RegistrationDTO>(getWorkingSetService().makeDTOs(regDto.registration().getBlockedBy()));
874
            }
875
            getView().setBlockingRegistrations(registrationUuid, blockingRegs);
876
        } else if(event.getProperty().equals(RegistrationItem.VALIDATION_PROBLEMS)){
877
            getView().openDetailsPopup("Validation Problems", regDto.getValidationProblems());
878
        }
879
    }
880

    
881
    @Override
882
    public ICdmEntityUuidCacher getCache() {
883
        return cache;
884
    }
885

    
886
    @Override
887
    public void addRootEntity(CdmBase entity) {
888
        rootEntities.add(entity);
889
        cache.load(entity);
890
    }
891

    
892
    @Override
893
    public Collection<CdmBase> getRootEntities() {
894
        return rootEntities;
895
    }
896

    
897
    @Override
898
    public void destroy() throws Exception {
899
        super.destroy();
900
        disposeCache();
901
    }
902

    
903
    @Override
904
    public void disposeCache() {
905
        cache.dispose();
906
    }
907

    
908
    public boolean canCreateNameRegistrationFor(TaxonName name) {
909
        return registrationWorkflowService.canCreateNameRegistrationFor(workingset, name);
910
    }
911

    
912
    public boolean checkWokingsetContainsProtologe(TaxonName name) {
913
        return registrationWorkflowService.checkWokingsetContainsProtologe(workingset, name);
914
    }
915
}
(10-10/18)