001// --------------------------------------------------------------------------------
002// Copyright 2002-2024 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.util.server.control;
018
019import com.echothree.model.data.user.common.pk.UserVisitPK;
020import com.echothree.util.common.command.BaseEditResult;
021import com.echothree.util.common.command.BaseResult;
022import com.echothree.util.common.command.EditMode;
023import com.echothree.util.common.form.BaseEdit;
024import com.echothree.util.common.form.BaseEditForm;
025import com.echothree.util.common.form.BaseSpec;
026import com.echothree.util.common.message.ExecutionErrors;
027import com.echothree.util.common.validation.FieldDefinition;
028import com.echothree.util.server.persistence.BaseEntity;
029import com.echothree.util.server.persistence.EntityPermission;
030import java.util.List;
031
032public abstract class BaseAbstractEditCommand<S extends BaseSpec, E extends BaseEdit, R extends BaseEditResult<E>, BE extends BaseEntity, LE extends BaseEntity>
033        extends BaseEditCommand<S, E> {
034    
035    protected BaseAbstractEditCommand(UserVisitPK userVisitPK, BaseEditForm<S, E> editForm, CommandSecurityDefinition commandSecurityDefinition, List<FieldDefinition> specFieldDefinitions,
036            List<FieldDefinition> editFieldDefinitions) {
037        super(userVisitPK, editForm, commandSecurityDefinition, specFieldDefinitions, editFieldDefinitions);
038    }
039    
040    protected abstract R getResult();
041    
042    protected abstract E getEdit();
043    
044    protected EntityPermission editModeToEntityPermission(EditMode editMode) {
045        EntityPermission entityPermission;
046        
047        if(editMode == EditMode.UPDATE) {
048            entityPermission = EntityPermission.READ_WRITE;
049        } else {
050            // EditMode.LOCK & EditMode.ABANDON
051            entityPermission = EntityPermission.READ_ONLY;
052        }
053        
054        return entityPermission;
055    }
056    
057    protected abstract BE getEntity(R result);
058    
059    protected abstract LE getLockEntity(BE baseEntity);
060    
061    protected abstract void fillInResult(R result, BE baseEntity);
062    
063    private void fillInResult(R result, BE baseEntity, LE lockEntity) {
064        fillInResult(result, baseEntity);
065        
066        if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.UPDATE)) {
067            result.setEntityLock(getEntityLockTransfer(lockEntity));
068        }
069    }
070    
071    protected abstract void doLock(E edit, BE baseEntity);
072    
073    // This check is called for both EditMode.LOCK and EditMode.UPDATE.
074    protected void canEdit(BE baseEntity) {
075        // Nothing.
076    }
077    
078    // This check is called for EditMode.UPDATE, after canEdit(...) is called.
079    protected void canUpdate(BE baseEntity) {
080        // Nothing.
081    }
082    
083    protected abstract void doUpdate(BE baseEntity);
084    
085    @Override
086    protected final BaseResult execute() {
087        R result = getResult();
088        BE baseEntity = getEntity(result);
089        
090        // getEntity(...) may set both SecurityMessages and ExecutionErrors.
091        if(!hasSecurityMessages() && !hasExecutionErrors()) {
092            LE lockEntity = getLockEntity(baseEntity);
093
094            switch(editMode) {
095                case LOCK -> {
096                    canEdit(baseEntity);
097
098                    // canEdit(...) may set both SecurityMessages and ExecutionErrors.
099                    if(!hasSecurityMessages()) {
100                        if(!hasExecutionErrors()) {
101                            if(lockEntity(lockEntity)) {
102                                edit = getEdit();
103                                result.setEdit(edit);
104                                doLock(edit, baseEntity);
105                            } else {
106                                addExecutionError(ExecutionErrors.EntityLockFailed.name());
107                            }
108                        }
109
110                        fillInResult(result, baseEntity, lockEntity);
111                    }
112                }
113                case ABANDON -> unlockEntity(lockEntity);
114                case UPDATE -> {
115                    canEdit(baseEntity);
116
117                    // canEdit(...) may set both SecurityMessages and ExecutionErrors.
118                    if(!hasSecurityMessages()) {
119                        if(!hasExecutionErrors()) {
120                            canUpdate(baseEntity);
121
122                            if(!hasExecutionErrors()) {
123                                if(lockEntityForUpdate(lockEntity)) {
124                                    try {
125                                        doUpdate(baseEntity);
126                                    } finally {
127                                        unlockEntity(lockEntity);
128                                    }
129                                } else {
130                                    addExecutionError(ExecutionErrors.EntityLockStale.name());
131                                }
132                            }
133                        }
134
135                        if(hasExecutionErrors()) {
136                            fillInResult(result, baseEntity, lockEntity);
137                        }
138                    }
139                }
140            }
141        }
142        
143        return result;
144    }
145    
146    R errorResult = null;
147    
148    @Override
149    protected void saveResultAfterEditValidatorErrors() {
150        errorResult = getResult();
151        
152        BE baseEntity = getEntity(errorResult);
153        LE lockEntity = getLockEntity(baseEntity);
154        
155        fillInResult(errorResult, baseEntity, lockEntity);
156    }
157    
158    @Override
159    protected BaseResult getBaseResultAfterErrors() {
160        return errorResult;
161    }
162    
163}