001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 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.control.user.item.server.command;
018
019import com.echothree.control.user.item.common.edit.ItemDescriptionEdit;
020import com.echothree.control.user.item.common.edit.ItemEditFactory;
021import com.echothree.control.user.item.common.form.EditItemDescriptionForm;
022import com.echothree.control.user.item.common.result.EditItemDescriptionResult;
023import com.echothree.control.user.item.common.result.ItemResultFactory;
024import com.echothree.control.user.item.common.spec.ItemDescriptionSpec;
025import com.echothree.model.control.core.common.EntityAttributeTypes;
026import com.echothree.model.control.core.common.MimeTypeUsageTypes;
027import com.echothree.model.control.core.server.control.MimeTypeControl;
028import com.echothree.model.control.item.server.control.ItemControl;
029import com.echothree.model.control.item.server.logic.ItemDescriptionLogic;
030import com.echothree.model.control.item.server.logic.ItemDescriptionLogic.ImageDimensions;
031import com.echothree.model.control.party.common.PartyTypes;
032import com.echothree.model.control.party.server.control.PartyControl;
033import com.echothree.model.control.security.common.SecurityRoleGroups;
034import com.echothree.model.control.security.common.SecurityRoles;
035import com.echothree.model.data.core.server.entity.MimeType;
036import com.echothree.model.data.item.server.entity.Item;
037import com.echothree.model.data.item.server.entity.ItemDescription;
038import com.echothree.model.data.item.server.entity.ItemDescriptionType;
039import com.echothree.model.data.item.server.entity.ItemImageType;
040import com.echothree.model.data.user.common.pk.UserVisitPK;
041import com.echothree.util.common.command.EditMode;
042import com.echothree.util.common.message.ExecutionErrors;
043import com.echothree.util.common.persistence.BasePK;
044import com.echothree.util.common.persistence.type.ByteArray;
045import com.echothree.util.common.validation.FieldDefinition;
046import com.echothree.util.common.validation.FieldType;
047import com.echothree.util.server.control.BaseAbstractEditCommand;
048import com.echothree.util.server.control.CommandSecurityDefinition;
049import com.echothree.util.server.control.PartyTypeDefinition;
050import com.echothree.util.server.control.SecurityRoleDefinition;
051import com.echothree.util.server.persistence.Session;
052import java.util.List;
053import javax.enterprise.context.Dependent;
054
055@Dependent
056public class EditItemDescriptionCommand
057        extends BaseAbstractEditCommand<ItemDescriptionSpec, ItemDescriptionEdit, EditItemDescriptionResult, ItemDescription, ItemDescription> {
058    
059    private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION;
060    private final static List<FieldDefinition> SPEC_FIELD_DEFINITIONS;
061    private final static List<FieldDefinition> EDIT_FIELD_DEFINITIONS;
062    
063    static {
064        COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(List.of(
065                new PartyTypeDefinition(PartyTypes.UTILITY.name(), null),
066                new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), List.of(
067                        new SecurityRoleDefinition(SecurityRoleGroups.ItemDescription.name(), SecurityRoles.Edit.name())
068                        ))
069                ));
070
071        SPEC_FIELD_DEFINITIONS = List.of(
072            new FieldDefinition("ItemName", FieldType.ENTITY_NAME, true, null, null),
073            new FieldDefinition("ItemDescriptionTypeName", FieldType.ENTITY_NAME, true, null, null),
074            new FieldDefinition("LanguageIsoName", FieldType.ENTITY_NAME, true, null, null)
075        );
076        
077        EDIT_FIELD_DEFINITIONS = List.of(
078            new FieldDefinition("MimeTypeName", FieldType.MIME_TYPE, false, null, null),
079            new FieldDefinition("ItemImageTypeName", FieldType.ENTITY_NAME, false, null, null),
080            new FieldDefinition("ClobDescription", FieldType.STRING, false, 1L, null),
081            new FieldDefinition("StringDescription", FieldType.STRING, false, 1L, 512L)
082        );
083    }
084    
085    /** Creates a new instance of EditItemDescriptionCommand */
086    public EditItemDescriptionCommand() {
087        super(COMMAND_SECURITY_DEFINITION, SPEC_FIELD_DEFINITIONS, EDIT_FIELD_DEFINITIONS);
088    }
089
090    @Override
091    public EditItemDescriptionResult getResult() {
092        return ItemResultFactory.getEditItemDescriptionResult();
093    }
094
095    @Override
096    public ItemDescriptionEdit getEdit() {
097        return ItemEditFactory.getItemDescriptionEdit();
098    }
099
100     ItemDescriptionType itemDescriptionType;
101     Item item;
102
103    @Override
104    public ItemDescription getEntity(EditItemDescriptionResult result) {
105        var itemControl = Session.getModelController(ItemControl.class);
106        ItemDescription itemDescription = null;
107        var itemDescriptionTypeName = spec.getItemDescriptionTypeName();
108
109        itemDescriptionType = itemControl.getItemDescriptionTypeByName(itemDescriptionTypeName);
110
111        if(itemDescriptionType != null) {
112            var itemName = spec.getItemName();
113
114            item = itemControl.getItemByName(itemName);
115
116            if(item != null) {
117                var partyControl = Session.getModelController(PartyControl.class);
118                var languageIsoName = spec.getLanguageIsoName();
119                var language = partyControl.getLanguageByIsoName(languageIsoName);
120
121                if(language != null) {
122                    if(editMode.equals(EditMode.LOCK) || editMode.equals(EditMode.ABANDON)) {
123                        itemDescription = itemControl.getItemDescription(itemDescriptionType, item, language);
124                    } else { // EditMode.UPDATE
125                        itemDescription = itemControl.getItemDescriptionForUpdate(itemDescriptionType, item, language);
126                    }
127
128                    if(itemDescription == null) {
129                        addExecutionError(ExecutionErrors.UnknownItemDescription.name(), itemDescriptionTypeName, itemName, languageIsoName);
130                    }
131                } else {
132                    addExecutionError(ExecutionErrors.UnknownLanguageIsoName.name(), languageIsoName);
133                }
134            } else {
135                addExecutionError(ExecutionErrors.UnknownItemName.name(), itemName);
136            }
137        } else {
138            addExecutionError(ExecutionErrors.UnknownItemDescriptionTypeName.name(), itemDescriptionTypeName);
139        }
140
141        return itemDescription;
142    }
143
144    @Override
145    public ItemDescription getLockEntity(ItemDescription itemDescription) {
146        return itemDescription;
147    }
148
149    @Override
150    public void fillInResult(EditItemDescriptionResult result, ItemDescription itemDescription) {
151        var itemControl = Session.getModelController(ItemControl.class);
152
153        result.setItemDescription(itemControl.getItemDescriptionTransfer(getUserVisit(), itemDescription));
154    }
155
156    MimeType mimeType;
157
158    @Override
159    public void doLock(ItemDescriptionEdit edit, ItemDescription itemDescription) {
160        var itemControl = Session.getModelController(ItemControl.class);
161        
162        mimeType = itemDescription.getLastDetail().getMimeType();
163
164        edit.setMimeTypeName(mimeType == null? null: mimeType.getLastDetail().getMimeTypeName());
165
166        if(mimeType == null) {
167            var itemStringDescription = itemControl.getItemStringDescription(itemDescription);
168
169            if(itemStringDescription != null) {
170                edit.setStringDescription(itemStringDescription.getStringDescription());
171            }
172        } else {
173            var entityAttributeTypeName = mimeType.getLastDetail().getEntityAttributeType().getEntityAttributeTypeName();
174
175            // EntityAttributeTypes.BLOB.name() does not return anything in edit
176            if(entityAttributeTypeName.equals(EntityAttributeTypes.CLOB.name())) {
177                var itemClobDescription = itemControl.getItemClobDescription(itemDescription);
178
179                if(itemClobDescription != null) {
180                    edit.setClobDescription(itemClobDescription.getClobDescription());
181                }
182            }
183        }
184    }
185
186    ItemImageType itemImageType;
187    ImageDimensions imageDimensions;
188
189    @Override
190    public void canUpdate(ItemDescription itemDescription) {
191        var mimeTypeName = edit.getMimeTypeName();
192
193        if(mimeTypeName == null) {
194            if(itemDescriptionType.getLastDetail().getMimeTypeUsageType() == null) {
195                var stringDescription = edit.getStringDescription();
196
197                if(stringDescription == null) {
198                    addExecutionError(ExecutionErrors.MissingStringDescription.name());
199                }
200            } else {
201                // No mimeTypeName was supplied, but yet we required a MimeTypeUsageType
202                addExecutionError(ExecutionErrors.InvalidMimeType.name());
203            }
204        } else {
205            var mimeTypeControl = Session.getModelController(MimeTypeControl.class);
206
207            mimeType = mimeTypeControl.getMimeTypeByName(mimeTypeName);
208
209            if(mimeType != null) {
210                var mimeTypeUsageType = itemDescriptionType.getLastDetail().getMimeTypeUsageType();
211
212                if(mimeTypeUsageType != null) {
213                    var mimeTypeUsage = mimeTypeControl.getMimeTypeUsage(mimeType, mimeTypeUsageType);
214
215                    if(mimeTypeUsage != null) {
216                        var mimeTypeDetail = mimeType.getLastDetail();
217                        var entityAttributeType = mimeTypeDetail.getEntityAttributeType();
218                        var entityAttributeTypeName = entityAttributeType.getEntityAttributeTypeName();
219
220                        if(entityAttributeTypeName.equals(EntityAttributeTypes.BLOB.name())) {
221                            var blobDescription = edit.getBlobDescription();
222
223                            if(blobDescription == null) {
224                                addExecutionError(ExecutionErrors.MissingBlobDescription.name());
225                            } else {
226                                var mimeTypeUsageTypeName = mimeTypeUsageType.getMimeTypeUsageTypeName();
227
228                                if(mimeTypeUsageTypeName.equals(MimeTypeUsageTypes.IMAGE.name())) {
229                                    var itemImageTypeName = edit.getItemImageTypeName();
230
231                                    if(itemImageTypeName != null) {
232                                        var itemControl = Session.getModelController(ItemControl.class);
233
234                                        itemImageType = itemControl.getItemImageTypeByName(itemImageTypeName);
235
236                                        if(itemImageType != null) {
237                                            imageDimensions = ItemDescriptionLogic.getInstance().getImageDimensions(mimeTypeDetail.getMimeTypeName(), blobDescription);
238
239                                            if(imageDimensions == null) {
240                                                addExecutionError(ExecutionErrors.InvalidImage.name());
241                                            } else {
242                                                var itemImageDescriptionType = itemControl.getItemImageDescriptionType(itemDescriptionType);
243
244                                                if(itemImageDescriptionType != null) {
245                                                    var minimumHeight = itemImageDescriptionType.getMinimumHeight();
246                                                    var minimumWidth = itemImageDescriptionType.getMinimumWidth();
247                                                    var maximumHeight = itemImageDescriptionType.getMaximumHeight();
248                                                    var maximumWidth = itemImageDescriptionType.getMaximumWidth();
249                                                    var imageHeight = imageDimensions.getHeight();
250                                                    var imageWidth = imageDimensions.getWidth();
251
252                                                    if(minimumHeight != null && imageHeight < minimumHeight) {
253                                                        addExecutionError(ExecutionErrors.ImageHeightLessThanMinimum.name(), imageHeight.toString(), minimumHeight.toString());
254                                                    }
255
256                                                    if(minimumWidth != null && imageWidth < minimumWidth) {
257                                                        addExecutionError(ExecutionErrors.ImageWidthLessThanMinimum.name(), imageWidth.toString(), minimumWidth.toString());
258                                                    }
259
260                                                    if(maximumHeight != null && imageHeight > maximumHeight) {
261                                                        addExecutionError(ExecutionErrors.ImageHeightGreaterThanMaximum.name(), imageHeight.toString(), maximumHeight.toString());
262                                                    }
263
264                                                    if(maximumWidth != null && imageWidth > maximumWidth) {
265                                                        addExecutionError(ExecutionErrors.ImageWidthGreaterThanMaximum.name(), imageWidth.toString(), maximumWidth.toString());
266                                                    }
267                                                }
268                                            }
269                                        } else {
270                                          addExecutionError(ExecutionErrors.UnknownItemImageTypeName.name(), itemImageTypeName);
271                                        }
272                                    } else {
273                                          addExecutionError(ExecutionErrors.MissingRequiredItemImageTypeName.name());
274                                    }
275                                }
276                            }
277                        } else if(entityAttributeTypeName.equals(EntityAttributeTypes.CLOB.name())) {
278                            var clobDescription = edit.getClobDescription();
279
280                            if(clobDescription == null) {
281                                addExecutionError(ExecutionErrors.MissingClobDescription.name());
282                            }
283                        } else {
284                            addExecutionError(ExecutionErrors.UnknownEntityAttributeTypeName.name(),
285                                    entityAttributeTypeName);
286                        }
287                    } else {
288                        addExecutionError(ExecutionErrors.UnknownMimeTypeUsage.name());
289                    }
290                } else {
291                    // mimeTypeName was supplied, and there shouldn't be one
292                    addExecutionError(ExecutionErrors.InvalidMimeType.name());
293                }
294            } else {
295                addExecutionError(ExecutionErrors.UnknownMimeTypeName.name(), mimeTypeName);
296            }
297        }
298    }
299
300    @Override
301    public void doUpdate(ItemDescription itemDescription) {
302        var itemControl = Session.getModelController(ItemControl.class);
303        var partyPK = getPartyPK();
304
305        if(mimeType == null) {
306            var stringDescription = edit.getStringDescription();
307
308            updateItemDescription(itemControl, item, null, partyPK, itemDescription, null, null, stringDescription);
309        } else {
310            var entityAttributeType = mimeType.getLastDetail().getEntityAttributeType();
311            var entityAttributeTypeName = entityAttributeType.getEntityAttributeTypeName();
312
313            if(entityAttributeTypeName.equals(EntityAttributeTypes.BLOB.name())) {
314                var blobDescription = edit.getBlobDescription();
315
316                updateItemDescription(itemControl, item, mimeType, partyPK, itemDescription, blobDescription, null, null);
317            } else {
318                if(entityAttributeTypeName.equals(EntityAttributeTypes.CLOB.name())) {
319                    var clobDescription = edit.getClobDescription();
320
321                    updateItemDescription(itemControl, item, mimeType, partyPK, itemDescription, null, clobDescription, null);
322                }
323            }
324        }
325    }
326
327    protected void updateItemDescription(ItemControl itemControl, Item item, MimeType mimeType, BasePK updatedBy,
328            ItemDescription itemDescription, ByteArray blobDescription, String clobDescription, String stringDescription) {
329        var itemDescriptionDetailValue = itemControl.getItemDescriptionDetailValueForUpdate(itemDescription);
330
331        itemDescriptionDetailValue.setMimeTypePK(mimeType == null? null: mimeType.getPrimaryKey());
332        itemControl.updateItemDescriptionFromValue(itemDescriptionDetailValue, updatedBy);
333
334        var itemBlobDescription = itemControl.getItemBlobDescriptionForUpdate(itemDescription);
335
336        if(itemBlobDescription != null) {
337            if(blobDescription == null) {
338                itemControl.deleteItemBlobDescription(itemBlobDescription, updatedBy);
339                itemControl.deleteItemImageDescriptionByItemDescription(itemDescription, updatedBy);
340            } else {
341                var itemBlobDescriptionValue = itemControl.getItemBlobDescriptionValue(itemBlobDescription);
342                var itemImageDescriptionValue = itemControl.getItemImageDescriptionValueForUpdate(itemDescription);
343
344                itemBlobDescriptionValue.setBlobDescription(blobDescription);
345                itemControl.updateItemBlobDescriptionFromValue(itemBlobDescriptionValue, updatedBy);
346
347                if(imageDimensions != null) {
348                    itemImageDescriptionValue.setHeight(imageDimensions.getHeight());
349                    itemImageDescriptionValue.setWidth(imageDimensions.getWidth());
350                    itemImageDescriptionValue.setScaledFromParent(false);
351
352                    itemControl.updateItemImageDescriptionFromValue(itemImageDescriptionValue, updatedBy);
353                }
354
355                if(itemBlobDescriptionValue.hasBeenModified()) {
356                    ItemDescriptionLogic.getInstance().deleteItemImageDescriptionChildren(itemDescription, updatedBy);
357                }
358            }
359        } else if(blobDescription != null) {
360            itemControl.createItemBlobDescription(itemDescription, blobDescription, updatedBy);
361
362            if(imageDimensions != null) {
363                itemControl.createItemImageDescription(itemDescription, itemImageType, imageDimensions.getHeight(), imageDimensions.getWidth(), false,
364                        updatedBy);
365            }
366
367            ItemDescriptionLogic.getInstance().deleteItemImageDescriptionChildren(itemDescription, updatedBy);
368        }
369
370        var itemClobDescription = itemControl.getItemClobDescriptionForUpdate(itemDescription);
371
372        if(itemClobDescription != null) {
373            if(clobDescription == null) {
374                itemControl.deleteItemClobDescription(itemClobDescription, updatedBy);
375            } else {
376                var itemClobDescriptionValue = itemControl.getItemClobDescriptionValue(itemClobDescription);
377
378                itemClobDescriptionValue.setClobDescription(clobDescription);
379                itemControl.updateItemClobDescriptionFromValue(itemClobDescriptionValue, updatedBy);
380            }
381        } else if(clobDescription != null) {
382            itemControl.createItemClobDescription(itemDescription, clobDescription, updatedBy);
383        }
384
385        var itemStringDescription = itemControl.getItemStringDescriptionForUpdate(itemDescription);
386
387        if(itemStringDescription != null) {
388            if(stringDescription == null) {
389                itemControl.deleteItemStringDescription(itemStringDescription, updatedBy);
390            } else {
391                var itemStringDescriptionValue = itemControl.getItemStringDescriptionValue(itemStringDescription);
392
393                itemStringDescriptionValue.setStringDescription(stringDescription);
394                itemControl.updateItemStringDescriptionFromValue(itemStringDescriptionValue, updatedBy);
395            }
396        } else if(stringDescription != null) {
397            itemControl.createItemStringDescription(itemDescription, stringDescription, updatedBy);
398        }
399    }
400    
401}