001// --------------------------------------------------------------------------------
002// Copyright 2002-2025 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.persistence;
018
019import com.echothree.model.control.core.common.ComponentVendors;
020import com.echothree.model.control.core.common.EntityTypes;
021import com.echothree.model.control.security.common.SecurityRoleGroups;
022import com.echothree.model.control.security.common.SecurityRoles;
023import com.echothree.model.control.security.server.logic.SecurityRoleLogic;
024import com.echothree.model.control.sequence.common.SequenceTypes;
025import com.echothree.model.control.sequence.server.logic.SequenceGeneratorLogic;
026import com.echothree.model.control.warehouse.server.control.WarehouseControl;
027import com.echothree.model.data.communication.common.pk.CommunicationEventPK;
028import com.echothree.model.data.communication.server.factory.CommunicationEventFactory;
029import com.echothree.model.data.contactlist.common.pk.PartyContactListPK;
030import com.echothree.model.data.contactlist.server.factory.PartyContactListFactory;
031import com.echothree.model.data.core.common.pk.ComponentVendorPK;
032import com.echothree.model.data.core.common.pk.EntityTypePK;
033import com.echothree.model.data.core.common.pk.MimeTypePK;
034import com.echothree.model.data.core.server.entity.EntityInstance;
035import com.echothree.model.data.core.server.factory.ComponentVendorFactory;
036import com.echothree.model.data.core.server.factory.EntityTypeFactory;
037import com.echothree.model.data.core.server.factory.MimeTypeFactory;
038import com.echothree.model.data.forum.common.pk.ForumGroupPK;
039import com.echothree.model.data.forum.common.pk.ForumMessagePK;
040import com.echothree.model.data.forum.common.pk.ForumPK;
041import com.echothree.model.data.forum.common.pk.ForumThreadPK;
042import com.echothree.model.data.forum.server.factory.ForumFactory;
043import com.echothree.model.data.forum.server.factory.ForumGroupFactory;
044import com.echothree.model.data.forum.server.factory.ForumMessageFactory;
045import com.echothree.model.data.forum.server.factory.ForumThreadFactory;
046import com.echothree.model.data.item.common.pk.ItemDescriptionPK;
047import com.echothree.model.data.item.common.pk.ItemPK;
048import com.echothree.model.data.item.server.factory.ItemDescriptionFactory;
049import com.echothree.model.data.item.server.factory.ItemFactory;
050import com.echothree.model.data.party.server.entity.Party;
051import com.echothree.model.data.subscription.common.pk.SubscriptionPK;
052import com.echothree.model.data.subscription.server.factory.SubscriptionFactory;
053import com.echothree.model.data.training.common.pk.PartyTrainingClassPK;
054import com.echothree.model.data.training.common.pk.TrainingClassPK;
055import com.echothree.model.data.training.server.factory.PartyTrainingClassFactory;
056import com.echothree.model.data.training.server.factory.TrainingClassFactory;
057import com.echothree.model.data.vendor.common.pk.VendorItemPK;
058import com.echothree.model.data.vendor.server.factory.VendorItemFactory;
059import com.echothree.model.data.warehouse.common.pk.LocationPK;
060import com.echothree.model.data.warehouse.server.factory.LocationFactory;
061import com.echothree.util.common.persistence.EntityNames;
062import com.echothree.util.common.persistence.Names;
063import com.echothree.util.common.persistence.Targets;
064import com.echothree.util.common.transfer.MapWrapper;
065import com.echothree.util.server.message.ExecutionErrorAccumulator;
066import com.echothree.util.server.persistence.translator.ComponentVendorTranslator;
067import com.echothree.util.server.persistence.translator.EntityInstanceAndNames;
068import com.echothree.util.server.persistence.translator.EntityInstanceTranslator;
069import com.echothree.util.server.persistence.translator.InvoiceNameTranslator;
070import com.echothree.util.server.persistence.translator.OrderNameTranslator;
071import com.echothree.util.server.persistence.translator.PartyNameTranslator;
072import com.echothree.util.server.persistence.translator.SequenceTypeTranslator;
073import java.util.Collections;
074import java.util.HashMap;
075import java.util.Map;
076
077public class EntityNamesUtils {
078    
079    private EntityNamesUtils() {
080        super();
081    }
082    
083    private static class EntityNamesUtilsHolder {
084        static EntityNamesUtils instance = new EntityNamesUtils();
085    }
086    
087    public static EntityNamesUtils getInstance() {
088        return EntityNamesUtilsHolder.instance;
089    }
090
091    private final static Map<String, ComponentVendorTranslator> componentVendorTranslators = new HashMap<>();
092
093    public static void addComponentVendorTranslator(String componentVendorName, ComponentVendorTranslator componentVendorTranslator) {
094        componentVendorTranslators.put(componentVendorName, componentVendorTranslator);
095    }
096
097    static {
098        Map<String, EntityInstanceTranslator> nameTranslators = new HashMap<>(16);
099
100        nameTranslators.put(EntityTypes.Invoice.name(), new InvoiceNameTranslator());
101        nameTranslators.put(EntityTypes.Order.name(), new OrderNameTranslator());
102        nameTranslators.put(EntityTypes.Party.name(), new PartyNameTranslator());
103
104        nameTranslators.put(EntityTypes.Item.name(), (final EntityInstance entityInstance) -> {
105            var itemDetail = ItemFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
106                    new ItemPK(entityInstance.getEntityUniqueId())).getLastDetail();
107            var names = new MapWrapper<String>(1);
108
109            names.put(Names.ItemName.name(), itemDetail.getItemName());
110
111            return new EntityNames(Targets.Item.name(), names);
112        });
113
114        nameTranslators.put(EntityTypes.ItemDescription.name(), (final EntityInstance entityInstance) -> {
115            var itemDescriptionDetail = ItemDescriptionFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
116                    new ItemDescriptionPK(entityInstance.getEntityUniqueId())).getLastDetail();
117            var names = new MapWrapper<String>(3);
118
119            names.put(Names.ItemDescriptionTypeName.name(), itemDescriptionDetail.getItemDescriptionType().getLastDetail().getItemDescriptionTypeName());
120            names.put(Names.ItemName.name(), itemDescriptionDetail.getItem().getLastDetail().getItemName());
121            names.put(Names.LanguageIsoName.name(), itemDescriptionDetail.getLanguage().getLanguageIsoName());
122
123            return new EntityNames(Targets.ItemDescription.name(), names);
124        });
125
126        nameTranslators.put(EntityTypes.ForumGroup.name(), (final EntityInstance entityInstance) -> {
127            var forumGroupDetail = ForumGroupFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
128                    new ForumGroupPK(entityInstance.getEntityUniqueId())).getLastDetail();
129            var names = new MapWrapper<String>(1);
130
131            names.put(Names.ForumGroupName.name(), forumGroupDetail.getForumGroupName());
132
133            return new EntityNames(Targets.ForumGroup.name(), names);
134        });
135
136        nameTranslators.put(EntityTypes.Forum.name(), (final EntityInstance entityInstance) -> {
137            var forumDetail = ForumFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
138                    new ForumPK(entityInstance.getEntityUniqueId())).getLastDetail();
139            var names = new MapWrapper<String>(1);
140
141            names.put(Names.ForumName.name(), forumDetail.getForumName());
142
143            return new EntityNames(Targets.Forum.name(), names);
144        });
145
146        nameTranslators.put(EntityTypes.ForumMessage.name(), (final EntityInstance entityInstance) -> {
147            var forumMessageDetail = ForumMessageFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
148                    new ForumMessagePK(entityInstance.getEntityUniqueId())).getLastDetail();
149            var names = new MapWrapper<String>(1);
150
151            names.put(Names.ForumMessageName.name(), forumMessageDetail.getForumMessageName());
152
153            return new EntityNames(Targets.ForumMessage.name(), names);
154        });
155
156        nameTranslators.put(EntityTypes.ForumThread.name(), (final EntityInstance entityInstance) -> {
157            var forumThreadDetail = ForumThreadFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
158                    new ForumThreadPK(entityInstance.getEntityUniqueId())).getLastDetail();
159            var names = new MapWrapper<String>(1);
160
161            names.put(Names.ForumThreadName.name(), forumThreadDetail.getForumThreadName());
162
163            return new EntityNames(Targets.ForumThread.name(), names);
164        });
165
166        nameTranslators.put(EntityTypes.TrainingClass.name(), (final EntityInstance entityInstance) -> {
167            var trainingClassDetail = TrainingClassFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
168                    new TrainingClassPK(entityInstance.getEntityUniqueId())).getLastDetail();
169            var names = new MapWrapper<String>(1);
170
171            names.put(Names.TrainingClassName.name(), trainingClassDetail.getTrainingClassName());
172
173            return new EntityNames(Targets.TrainingClass.name(), names);
174        });
175
176        nameTranslators.put(EntityTypes.PartyTrainingClass.name(), (final EntityInstance entityInstance) -> {
177            var partyTrainingClassDetail = PartyTrainingClassFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
178                    new PartyTrainingClassPK(entityInstance.getEntityUniqueId())).getLastDetail();
179            var names = new MapWrapper<String>(1);
180
181            names.put(Names.PartyTrainingClassName.name(), partyTrainingClassDetail.getPartyTrainingClassName());
182
183            return new EntityNames(Targets.PartyTrainingClass.name(), names);
184        });
185
186        nameTranslators.put(EntityTypes.CommunicationEvent.name(), (final EntityInstance entityInstance) -> {
187            var communicationEventDetail = CommunicationEventFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
188                    new CommunicationEventPK(entityInstance.getEntityUniqueId())).getLastDetail();
189            var names = new MapWrapper<String>(1);
190
191            names.put(Names.CommunicationEventName.name(), communicationEventDetail.getCommunicationEventName());
192
193            return new EntityNames(Targets.CommunicationEvent.name(), names);
194        });
195
196        nameTranslators.put(EntityTypes.VendorItem.name(), (final EntityInstance entityInstance) -> {
197            var vendorItemDetail = VendorItemFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
198                    new VendorItemPK(entityInstance.getEntityUniqueId())).getLastDetail();
199            var names = new MapWrapper<String>(2);
200
201            names.put(Names.VendorItemName.name(), vendorItemDetail.getVendorItemName());
202            names.put(Names.PartyName.name(), vendorItemDetail.getVendorParty().getLastDetail().getPartyName());
203
204            return new EntityNames(Targets.VendorItem.name(), names);
205        });
206
207        nameTranslators.put(EntityTypes.PartyContactList.name(), (final EntityInstance entityInstance) -> {
208            var partyContactListDetail = PartyContactListFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
209                    new PartyContactListPK(entityInstance.getEntityUniqueId())).getLastDetail();
210            var names = new MapWrapper<String>(2);
211
212            names.put(Names.PartyName.name(), partyContactListDetail.getParty().getLastDetail().getPartyName());
213            names.put(Names.ContactListName.name(), partyContactListDetail.getContactList().getLastDetail().getContactListName());
214
215            return new EntityNames(Targets.PartyContactList.name(), names);
216        });
217
218        nameTranslators.put(EntityTypes.Subscription.name(), (final EntityInstance entityInstance) -> {
219            var subscription = SubscriptionFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
220                    new SubscriptionPK(entityInstance.getEntityUniqueId()));
221            var names = new MapWrapper<String>(1);
222
223            names.put(Names.SubscriptionName.name(), subscription.getLastDetail().getSubscriptionName());
224
225            return new EntityNames(Targets.Subscription.name(), names);
226        });
227
228        nameTranslators.put(EntityTypes.MimeType.name(), (final EntityInstance entityInstance) -> {
229            var mimeType = MimeTypeFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
230                    new MimeTypePK(entityInstance.getEntityUniqueId()));
231            var names = new MapWrapper<String>(1);
232
233            names.put(Names.MimeTypeName.name(), mimeType.getLastDetail().getMimeTypeName());
234
235            return new EntityNames(Targets.MimeType.name(), names);
236        });
237
238        nameTranslators.put(EntityTypes.Location.name(), (final EntityInstance entityInstance) -> {
239            var warehouseControl = Session.getModelController(WarehouseControl.class);
240            var location = LocationFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
241                    new LocationPK(entityInstance.getEntityUniqueId()));
242            var names = new MapWrapper<String>(2);
243            var locationDetail = location.getLastDetail();
244
245            names.put(Names.WarehouseName.name(), warehouseControl.getWarehouse(locationDetail.getWarehouseParty()).getWarehouseName());
246            names.put(Names.LocationName.name(), locationDetail.getLocationName());
247
248            return new EntityNames(Targets.Location.name(), names);
249        });
250
251        nameTranslators.put(EntityTypes.ComponentVendor.name(), (final EntityInstance entityInstance) -> {
252            var componentVendor = ComponentVendorFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
253                    new ComponentVendorPK(entityInstance.getEntityUniqueId()));
254            var names = new MapWrapper<String>(1);
255
256            names.put(Names.ComponentVendorName.name(), componentVendor.getLastDetail().getComponentVendorName());
257
258            return new EntityNames(Targets.ComponentVendor.name(), names);
259        });
260
261        nameTranslators.put(EntityTypes.EntityType.name(), (final EntityInstance entityInstance) -> {
262            var entityType = EntityTypeFactory.getInstance().getEntityFromPK(EntityPermission.READ_ONLY,
263                    new EntityTypePK(entityInstance.getEntityUniqueId()));
264            var names = new MapWrapper<String>(2);
265            var entityTypeDetail = entityType.getLastDetail();
266
267            names.put(Names.ComponentVendorName.name(), entityTypeDetail.getComponentVendor().getLastDetail().getComponentVendorName());
268            names.put(Names.EntityTypeName.name(), entityTypeDetail.getEntityTypeName());
269
270            return new EntityNames(Targets.EntityType.name(), names);
271        });
272
273        nameTranslators = Collections.unmodifiableMap(nameTranslators);
274
275        addComponentVendorTranslator(ComponentVendors.ECHO_THREE.name(), new ComponentVendorTranslator(nameTranslators));
276    }
277
278    // Entry point from the Identify UC. Permissions have already been checked at this point.
279    public EntityInstanceAndNames getEntityNames(final EntityInstance entityInstance) {
280        EntityNames result = null;
281        var entityType = entityInstance.getEntityType();
282        var componentVendor = entityType.getLastDetail().getComponentVendor();
283        var componentVendorName = componentVendor.getLastDetail().getComponentVendorName();
284
285        var componentVendorTranslator = componentVendorTranslators.get(componentVendorName);
286
287        if(componentVendorTranslator != null) {
288            var nameTranslator = componentVendorTranslator.getNameTranslators().get(entityType.getLastDetail().getEntityTypeName());
289
290            if(nameTranslator != null) {
291                result = nameTranslator.getNames(entityInstance);
292            }
293        }
294
295        return result == null ? null : new EntityInstanceAndNames(entityInstance, result);
296    }
297    
298    private final static Map<String, SequenceTypeTranslator> sequenceTypeTranslators;
299
300    private final static InvoiceNameTranslator INVOICE_NAME_TRANSLATOR = new InvoiceNameTranslator();
301    private final static OrderNameTranslator ORDER_NAME_TRANSLATOR = new OrderNameTranslator();
302    private final static PartyNameTranslator PARTY_NAME_TRANSLATOR = new PartyNameTranslator();
303
304    static {
305        var sequenceTypeTranslatorsMap = new HashMap<String, SequenceTypeTranslator>(7);
306
307        sequenceTypeTranslatorsMap.put(SequenceTypes.PURCHASE_INVOICE.name(), INVOICE_NAME_TRANSLATOR);
308        sequenceTypeTranslatorsMap.put(SequenceTypes.SALES_INVOICE.name(), INVOICE_NAME_TRANSLATOR);
309        sequenceTypeTranslatorsMap.put(SequenceTypes.PURCHASE_ORDER.name(), ORDER_NAME_TRANSLATOR);
310        sequenceTypeTranslatorsMap.put(SequenceTypes.SALES_ORDER.name(), ORDER_NAME_TRANSLATOR);
311        sequenceTypeTranslatorsMap.put(SequenceTypes.WISHLIST.name(), ORDER_NAME_TRANSLATOR);
312        sequenceTypeTranslatorsMap.put(SequenceTypes.CUSTOMER.name(), PARTY_NAME_TRANSLATOR);
313        sequenceTypeTranslatorsMap.put(SequenceTypes.EMPLOYEE.name(), PARTY_NAME_TRANSLATOR);
314        
315        sequenceTypeTranslators = Collections.unmodifiableMap(sequenceTypeTranslatorsMap);
316    }
317    
318    private EntityInstanceAndNames getEntityNames(final String sequenceTypeName, final String value,
319            final boolean includeEntityInstance) {
320        EntityInstanceAndNames result = null;
321        var sequenceTypeTranslator = sequenceTypeTranslators.get(sequenceTypeName);
322        
323        if(sequenceTypeTranslator != null) {
324            result = sequenceTypeTranslator.getNames(sequenceTypeName, value, includeEntityInstance);
325        }
326        
327        return result;
328    }
329
330    // Entry point from the Identify UC. Permissions have NOT been checked at this point.
331    public EntityInstanceAndNames getEntityNames(final ExecutionErrorAccumulator eea, final Party requestingParty,
332            final String value, final boolean includeEntityInstance) {
333        EntityInstanceAndNames result = null;
334        var sequenceType = SequenceGeneratorLogic.getInstance().identifySequenceType(value);
335
336        if(sequenceType != null) {
337            var sequenceTypeName = sequenceType.getLastDetail().getSequenceTypeName();
338            var hasAccess = false;
339
340            if(sequenceTypeName.equals(SequenceTypes.PURCHASE_INVOICE.name())
341                    && SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(eea, requestingParty,
342                    SecurityRoleGroups.PurchaseInvoice.name(), SecurityRoles.Search.name())) {
343                hasAccess = true;
344            } else if(sequenceTypeName.equals(SequenceTypes.SALES_INVOICE.name())
345                    && SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(eea, requestingParty,
346                    SecurityRoleGroups.SalesOrder.name(), SecurityRoles.Search.name())) {
347                hasAccess = true;
348            } else if(sequenceTypeName.equals(SequenceTypes.PURCHASE_ORDER.name())
349                    && SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(eea, requestingParty,
350                    SecurityRoleGroups.PurchaseOrder.name(), SecurityRoles.Search.name())) {
351                hasAccess = true;
352            } else if(sequenceTypeName.equals(SequenceTypes.SALES_ORDER.name())
353                    && SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(eea, requestingParty,
354                    SecurityRoleGroups.SalesOrder.name(), SecurityRoles.Search.name())) {
355                hasAccess = true;
356            } else if(sequenceTypeName.equals(SequenceTypes.WISHLIST.name())
357                    && SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(eea, requestingParty,
358                    SecurityRoleGroups.Wishlist.name(), SecurityRoles.Search.name())) {
359                hasAccess = true;
360            } else if(sequenceTypeName.equals(SequenceTypes.CUSTOMER.name())
361                    && SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(eea, requestingParty,
362                    SecurityRoleGroups.Customer.name(), SecurityRoles.Search.name())) {
363                hasAccess = true;
364            } else if(sequenceTypeName.equals(SequenceTypes.EMPLOYEE.name())
365                    && SecurityRoleLogic.getInstance().hasSecurityRoleUsingNames(eea, requestingParty,
366                    SecurityRoleGroups.Employee.name(), SecurityRoles.Search.name())) {
367                hasAccess = true;
368            }
369
370            result = hasAccess ? getEntityNames(sequenceTypeName, value, includeEntityInstance) : null;
371        }
372        
373        return result;
374    }
375    
376}