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