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.transfer; 018 019import com.echothree.model.control.comment.common.transfer.CommentListWrapper; 020import com.echothree.model.control.comment.server.control.CommentControl; 021import com.echothree.model.control.core.common.transfer.EntityAliasTypeTransfer; 022import com.echothree.model.control.core.common.transfer.EntityAttributeGroupTransfer; 023import com.echothree.model.control.core.server.control.CoreControl; 024import com.echothree.model.control.core.server.control.EntityAliasControl; 025import com.echothree.model.control.core.server.control.EntityInstanceControl; 026import com.echothree.model.control.rating.common.transfer.RatingListWrapper; 027import com.echothree.model.control.rating.server.control.RatingControl; 028import com.echothree.model.control.tag.common.transfer.TagScopeTransfer; 029import com.echothree.model.control.tag.server.control.TagControl; 030import com.echothree.model.control.uom.server.control.UomControl; 031import com.echothree.model.control.user.server.control.UserControl; 032import com.echothree.model.control.workeffort.server.control.WorkEffortControl; 033import com.echothree.model.data.accounting.server.entity.Currency; 034import com.echothree.model.data.core.server.entity.EntityInstance; 035import com.echothree.model.data.party.common.pk.PartyPK; 036import com.echothree.model.data.party.server.entity.DateTimeFormat; 037import com.echothree.model.data.party.server.entity.Language; 038import com.echothree.model.data.party.server.entity.Party; 039import com.echothree.model.data.party.server.entity.TimeZone; 040import com.echothree.model.data.uom.server.entity.UnitOfMeasureKind; 041import com.echothree.model.data.user.server.entity.UserVisit; 042import com.echothree.util.common.exception.TransferOptionDependencyException; 043import com.echothree.util.common.string.Inet4AddressUtils; 044import com.echothree.util.common.transfer.BaseOptions; 045import com.echothree.util.common.transfer.BaseTransfer; 046import com.echothree.util.common.transfer.ListWrapper; 047import com.echothree.util.common.transfer.MapWrapper; 048import com.echothree.util.server.persistence.BaseEntity; 049import com.echothree.util.server.persistence.Session; 050import com.echothree.util.server.persistence.ThreadSession; 051import com.echothree.util.server.string.DateUtils; 052import com.echothree.util.server.string.PercentUtils; 053import com.echothree.util.server.string.UnitOfMeasureUtils; 054import java.util.HashMap; 055import java.util.Map; 056import org.apache.commons.logging.Log; 057import org.apache.commons.logging.LogFactory; 058 059public abstract class BaseTransferCache<K extends BaseEntity, V extends BaseTransfer> { 060 061 private Log log = null; 062 063 protected Session session; 064 protected Map<K, V> transferCache; 065 066 private UomControl uomControl; 067 private UserControl userControl; 068 private Party party; 069 private Language language; 070 private Currency currency; 071 private TimeZone timeZone; 072 private DateTimeFormat dateTimeFormat; 073 074 boolean includeEntityInstance; 075 boolean includeEntityAppearance; 076 boolean includeEntityVisit; 077 boolean includeNames; 078 boolean includeUuid; 079 boolean includeEntityAliasTypes; 080 boolean includeEntityAttributeGroups; 081 boolean includeTagScopes; 082 083 /** Creates a new instance of BaseTransferCache */ 084 protected BaseTransferCache() { 085 session = ThreadSession.currentSession(); 086 transferCache = new HashMap<>(); 087 088 var options = session.getOptions(); 089 if(options != null) { 090 includeEntityAliasTypes = options.contains(BaseOptions.BaseIncludeEntityAliasTypes); 091 includeEntityAttributeGroups = options.contains(BaseOptions.BaseIncludeEntityAttributeGroups); 092 includeTagScopes = options.contains(BaseOptions.BaseIncludeTagScopes); 093 } 094 } 095 096 protected Log getLog() { 097 if(log == null) { 098 log = LogFactory.getLog(this.getClass()); 099 } 100 101 return log; 102 } 103 104 protected void put(final UserVisit userVisit, final K key, final V value, final EntityInstance entityInstance) { 105 transferCache.put(key, value); 106 107 if(includeEntityInstance) { 108 setupEntityInstance(userVisit, key, entityInstance, value); 109 } 110 } 111 112 protected void put(final UserVisit userVisit, final K key, final V value) { 113 put(userVisit, key, value, null); 114 } 115 116 protected V get(Object key) { 117 return transferCache.get(key); 118 } 119 120 protected UomControl getUomControl() { 121 if(uomControl == null) { 122 uomControl = Session.getModelController(UomControl.class); 123 } 124 125 return uomControl; 126 } 127 128 protected UserControl getUserControl() { 129 if(userControl == null) { 130 userControl = Session.getModelController(UserControl.class); 131 } 132 133 return userControl; 134 } 135 136 protected PartyPK getPartyPK(final UserVisit userVisit) { 137 if(party == null) { 138 getParty(userVisit); 139 } 140 141 return party == null? null: party.getPrimaryKey(); 142 } 143 144 protected Party getParty(final UserVisit userVisit) { 145 if(party == null) { 146 party = getUserControl().getPartyFromUserVisit(userVisit); 147 } 148 149 return party; 150 } 151 152 protected Language getLanguage(final UserVisit userVisit) { 153 if(language == null) { 154 language = getUserControl().getPreferredLanguageFromUserVisit(userVisit); 155 } 156 157 return language; 158 } 159 160 protected Currency getCurrency(final UserVisit userVisit) { 161 if(currency == null) { 162 currency = getUserControl().getPreferredCurrencyFromUserVisit(userVisit); 163 } 164 165 return currency; 166 } 167 168 protected TimeZone getTimeZone(final UserVisit userVisit) { 169 if(timeZone == null) { 170 timeZone = getUserControl().getPreferredTimeZoneFromUserVisit(userVisit); 171 } 172 173 return timeZone; 174 } 175 176 protected DateTimeFormat getDateTimeFormat(final UserVisit userVisit) { 177 if(dateTimeFormat == null) { 178 dateTimeFormat = getUserControl().getPreferredDateTimeFormatFromUserVisit(userVisit); 179 } 180 181 return dateTimeFormat; 182 } 183 184 protected String formatTypicalDateTime(final UserVisit userVisit, Long time) { 185 return DateUtils.getInstance().formatTypicalDateTime(userVisit, time); 186 } 187 188 protected String formatFractionalPercent(Integer percent) { 189 return PercentUtils.getInstance().formatFractionalPercent(percent); 190 } 191 192 protected String formatInet4Address(Integer inet4Address) { 193 return Inet4AddressUtils.getInstance().formatInet4Address(inet4Address); 194 } 195 196 protected String formatUnitOfMeasure(final UserVisit userVisit, UnitOfMeasureKind unitOfMeasureKind, Long measure) { 197 return UnitOfMeasureUtils.getInstance().formatUnitOfMeasure(userVisit, unitOfMeasureKind, measure); 198 } 199 200 /** 201 * Returns the includeEntityAliasTypes. 202 * @return the includeEntityAliasTypes 203 */ 204 protected boolean getIncludeEntityAliasTypes() { 205 return includeEntityAliasTypes; 206 } 207 208 /** 209 * Sets the includeEntityAliasTypes. 210 * @param includeEntityAliasTypes the includeEntityAliasTypes to set 211 */ 212 protected void setIncludeEntityAliasTypes(boolean includeEntityAliasTypes) { 213 this.includeEntityAliasTypes = includeEntityAliasTypes; 214 } 215 216 protected void setupEntityAliasTypes(final UserVisit userVisit, EntityInstance entityInstance, V transfer) { 217 var entityAliasControl = Session.getModelController(EntityAliasControl.class); 218 var entityAliasTypeTransfers = entityAliasControl.getEntityAliasTypeTransfersByEntityType(userVisit, entityInstance.getEntityType(), entityInstance); 219 var mapWrapper = new MapWrapper<EntityAliasTypeTransfer>(entityAliasTypeTransfers.size()); 220 221 entityAliasTypeTransfers.forEach((entityAliasTypeTransfer) -> { 222 mapWrapper.put(entityAliasTypeTransfer.getEntityAliasTypeName(), entityAliasTypeTransfer); 223 }); 224 225 transfer.setEntityAliasTypes(mapWrapper); 226 } 227 228 /** 229 * Returns the includeEntityAttributeGroups. 230 * @return the includeEntityAttributeGroups 231 */ 232 protected boolean getIncludeEntityAttributeGroups() { 233 return includeEntityAttributeGroups; 234 } 235 236 /** 237 * Sets the includeEntityAttributeGroups. 238 * @param includeEntityAttributeGroups the includeEntityAttributeGroups to set 239 */ 240 protected void setIncludeEntityAttributeGroups(boolean includeEntityAttributeGroups) { 241 this.includeEntityAttributeGroups = includeEntityAttributeGroups; 242 } 243 244 protected void setupEntityAttributeGroups(final UserVisit userVisit, EntityInstance entityInstance, V transfer) { 245 var coreControl = Session.getModelController(CoreControl.class); 246 var entityAttributeGroupTransfers = coreControl.getEntityAttributeGroupTransfersByEntityType(userVisit, entityInstance.getEntityType(), entityInstance); 247 var mapWrapper = new MapWrapper<EntityAttributeGroupTransfer>(entityAttributeGroupTransfers.size()); 248 249 entityAttributeGroupTransfers.forEach((entityAttributeGroupTransfer) -> { 250 mapWrapper.put(entityAttributeGroupTransfer.getEntityAttributeGroupName(), entityAttributeGroupTransfer); 251 }); 252 253 transfer.setEntityAttributeGroups(mapWrapper); 254 } 255 256 /** 257 * Returns the includeTagScopes. 258 * @return the includeTagScopes 259 */ 260 protected boolean getIncludeTagScopes() { 261 return includeTagScopes; 262 } 263 264 /** 265 * Sets the includeTagScopes. 266 * @param includeTagScopes the includeTagScopes to set 267 */ 268 protected void setIncludeTagScopes(boolean includeTagScopes) { 269 this.includeTagScopes = includeTagScopes; 270 } 271 272 protected void setupTagScopes(final UserVisit userVisit, EntityInstance entityInstance, V transfer) { 273 var tagControl = Session.getModelController(TagControl.class); 274 var tagScopes = tagControl.getTagScopesByEntityType(entityInstance.getEntityType()); 275 var mapWrapper = new MapWrapper<TagScopeTransfer>(tagScopes.size()); 276 277 tagScopes.stream().map((tagScope) -> { 278 // We get a copy of the TagScopeTransfer since we'll be modifying a field on it. Because there may be multiple instances of the 279 // TransferObject that we're building at this point, they may each have their own List of Tags within a given TagScope, so we do 280 // not want the tags property to be shared among all of them. 281 var tagScopeTransfer = tagControl.getTagScopeTransfer(userVisit, tagScope).copy(); 282 tagScopeTransfer.setTags(new ListWrapper<>(tagControl.getTagTransfersByTagScopeAndEntityInstance(userVisit, tagScope, entityInstance))); 283 return tagScopeTransfer; 284 }).forEach((tagScopeTransfer) -> { 285 mapWrapper.put(tagScopeTransfer.getTagScopeName(), tagScopeTransfer); 286 }); 287 288 transfer.setTagScopes(mapWrapper); 289 } 290 291 292 /** 293 * Returns the includeEntityInstance. 294 * @return the includeEntityInstance 295 */ 296 protected boolean getIncludeEntityInstance() { 297 return includeEntityInstance; 298 } 299 300 /** 301 * Sets the setupBaseTransfer. 302 * @param includeEntityInstance the setupBaseTransfer to set 303 */ 304 protected void setIncludeEntityInstance(boolean includeEntityInstance) { 305 this.includeEntityInstance = includeEntityInstance; 306 } 307 308 /** 309 * Returns the includeEntityAppearance. 310 * @return the includeEntityAppearance 311 */ 312 protected boolean getIncludeEntityAppearance() { 313 return includeEntityAppearance; 314 } 315 316 /** 317 * Sets the includeEntityAppearance. 318 * @param includeEntityAppearance the includeEntityAppearance to set 319 */ 320 protected void setIncludeEntityAppearance(boolean includeEntityAppearance) { 321 this.includeEntityAppearance = includeEntityAppearance; 322 } 323 324 /** 325 * Returns the includeEntityVisit. 326 * @return the includeEntityVisit 327 */ 328 protected boolean getIncludeEntityVisit() { 329 return includeEntityVisit; 330 } 331 332 /** 333 * Sets the includeEntityVisit. 334 * @param includeEntityVisit the includeEntityVisit to set 335 */ 336 protected void setIncludeEntityVisit(boolean includeEntityVisit) { 337 this.includeEntityVisit = includeEntityVisit; 338 } 339 340 /** 341 * Returns the includeNames. 342 * @return the includeNames 343 */ 344 protected boolean getIncludeNames() { 345 return includeNames; 346 } 347 348 /** 349 * Sets the includeNames. 350 * @param includeNames the includeNames to set 351 */ 352 protected void setIncludeNames(boolean includeNames) { 353 this.includeNames = includeNames; 354 } 355 356 /** 357 * Returns the includeUuid. 358 * @return the includeUuid 359 */ 360 protected boolean getIncludeUuid() { 361 return includeUuid; 362 } 363 364 /** 365 * Sets the includeUuid. 366 * @param includeUuid the includeUuid to set 367 */ 368 protected void setIncludeUuid(boolean includeUuid) { 369 this.includeUuid = includeUuid; 370 } 371 372 protected void setupEntityInstance(final UserVisit userVisit, final K baseEntity, EntityInstance entityInstance, final V transfer) { 373 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 374 375 if(entityInstance == null) { 376 entityInstance = entityInstanceControl.getEntityInstanceByBasePK(baseEntity.getPrimaryKey()); 377 } 378 379 // Check to make sure entityInstance is not null. This may happen in a case where a non-versioned entity was 380 // converted to a versioned one. 381 if(entityInstance != null) { 382 transfer.setEntityInstance(entityInstanceControl.getEntityInstanceTransfer(userVisit, entityInstance, includeEntityAppearance, 383 includeEntityVisit, includeNames, includeUuid)); 384 385 if(includeEntityAliasTypes || includeEntityAttributeGroups || includeTagScopes) { 386 if(includeEntityAliasTypes) { 387 setupEntityAliasTypes(userVisit, entityInstance, transfer); 388 } 389 390 if(includeEntityAttributeGroups) { 391 setupEntityAttributeGroups(userVisit, entityInstance, transfer); 392 } 393 394 if(includeTagScopes) { 395 setupTagScopes(userVisit, entityInstance, transfer); 396 } 397 } 398 } 399 } 400 401 protected EntityInstance setupComments(final UserVisit userVisit, final K commentedEntity, EntityInstance commentedEntityInstance, final V transfer, final String commentTypeName) { 402 var commentControl = Session.getModelController(CommentControl.class); 403 404 if(commentedEntityInstance == null) { 405 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 406 407 commentedEntityInstance = entityInstanceControl.getEntityInstanceByBasePK(commentedEntity.getPrimaryKey()); 408 } 409 410 var commentType = commentControl.getCommentTypeByName(commentedEntityInstance.getEntityType(), commentTypeName); 411 transfer.addComments(commentTypeName, new CommentListWrapper(commentControl.getCommentTypeTransfer(userVisit, commentType), 412 commentControl.getCommentTransfersByCommentedEntityInstanceAndCommentType(userVisit, commentedEntityInstance, commentType))); 413 414 return commentedEntityInstance; 415 } 416 417 protected EntityInstance setupRatings(final UserVisit userVisit, final K ratedEntity, EntityInstance ratedEntityInstance, final V transfer, final String ratingTypeName) { 418 var ratingControl = Session.getModelController(RatingControl.class); 419 420 if(ratedEntityInstance == null) { 421 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 422 423 ratedEntityInstance = entityInstanceControl.getEntityInstanceByBasePK(ratedEntity.getPrimaryKey()); 424 } 425 426 var ratingType = ratingControl.getRatingTypeByName(ratedEntityInstance.getEntityType(), ratingTypeName); 427 transfer.addRatings(ratingTypeName, new RatingListWrapper(ratingControl.getRatingTypeTransfer(userVisit, ratingType), 428 ratingControl.getRatingTransfersByRatedEntityInstanceAndRatingType(userVisit, ratedEntityInstance, ratingType))); 429 430 return ratedEntityInstance; 431 } 432 433 protected EntityInstance setupOwnedWorkEfforts(final UserVisit userVisit, final K baseEntity, EntityInstance owningEntityInstance, final V transfer) { 434 var workEffortControl = Session.getModelController(WorkEffortControl.class); 435 436 if(owningEntityInstance == null) { 437 var entityInstanceControl = Session.getModelController(EntityInstanceControl.class); 438 439 owningEntityInstance = entityInstanceControl.getEntityInstanceByBasePK(baseEntity.getPrimaryKey()); 440 } 441 442 for(var workEffort: workEffortControl.getWorkEffortTransfersByOwningEntityInstance(userVisit, owningEntityInstance)) { 443 transfer.addOwnedWorkEffort(workEffort.getWorkEffortScope().getWorkEffortType().getWorkEffortTypeName(), workEffort); 444 } 445 446 return owningEntityInstance; 447 } 448 449 protected void verifyOptionDependency(String dependentOption, String dependsOnOption) { 450 var options = session.getOptions(); 451 452 if(!options.contains(dependsOnOption)) { 453 // Throwing an Exception for this seems harsh, but failure to meet the requirements could result in an NPE or other Exceptions. 454 throw new TransferOptionDependencyException(dependentOption + " requires that " + dependsOnOption + " be set as well"); 455 } 456 } 457 458}