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