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