content: /*
   * Copyright 2010-2025 Hyland Software, Inc. and its affiliates.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.activiti.engine.impl.persistence.entity.data;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
  import org.activiti.engine.impl.db.DbSqlSession;
  import org.activiti.engine.impl.persistence.AbstractManager;
  import org.activiti.engine.impl.persistence.CachedEntityMatcher;
  import org.activiti.engine.impl.persistence.SingleCachedEntityMatcher;
  import org.activiti.engine.impl.persistence.cache.CachedEntity;
  import org.activiti.engine.impl.persistence.cache.EntityCache;
  import org.activiti.engine.impl.persistence.entity.Entity;
  
  /**
  
   */
  public abstract class AbstractDataManager<EntityImpl extends Entity>
      extends AbstractManager
      implements DataManager<EntityImpl> {
  
      public AbstractDataManager(ProcessEngineConfigurationImpl processEngineConfiguration) {
          super(processEngineConfiguration);
      }
  
      public abstract Class<? extends EntityImpl> getManagedEntityClass();
  
      public List<Class<? extends EntityImpl>> getManagedEntitySubClasses() {
          return null;
      }
  
      protected DbSqlSession getDbSqlSession() {
          return getSession(DbSqlSession.class);
      }
  
      protected EntityCache getEntityCache() {
          return getSession(EntityCache.class);
      }
  
      @Override
      public EntityImpl findById(String entityId) {
          if (entityId == null) {
              return null;
          }
  
          // Cache
          EntityImpl cachedEntity = getEntityCache().findInCache(getManagedEntityClass(), entityId);
          if (cachedEntity != null) {
              return cachedEntity;
          }
  
          // Database
          return getDbSqlSession().selectById(getManagedEntityClass(), entityId, false);
      }
  
      @Override
      public void insert(EntityImpl entity) {
          getDbSqlSession().insert(entity);
      }
  
      public EntityImpl update(EntityImpl entity) {
          getDbSqlSession().update(entity);
          return entity;
      }
  
      @Override
      public void delete(String id) {
          EntityImpl entity = findById(id);
          delete(entity);
      }
  
      @Override
      public void delete(EntityImpl entity) {
          getDbSqlSession().delete(entity);
      }
  
      @SuppressWarnings("unchecked")
      protected EntityImpl getEntity(
          String selectQuery,
          Object parameter,
          SingleCachedEntityMatcher<EntityImpl> cachedEntityMatcher,
          boolean checkDatabase
      ) {
          // Cache
          for (EntityImpl cachedEntity : getEntityCache().findInCache(getManagedEntityClass())) {
              if (cachedEntityMatcher.isRetained(cachedEntity, parameter)) {
                  return cachedEntity;
              }
          }
  
          // Database
          if (checkDatabase) {
              return (EntityImpl) getDbSqlSession().selectOne(selectQuery, parameter);
          }
  
          return null;
      }
  
      /**
       * Gets a list by querying the database and the cache using {@link CachedEntityMatcher}.
       * First, the entities are fetched from the database using the provided query.
       * The cache is then queried for the entities of the same type. If an entity matches
       * the {@link CachedEntityMatcher} condition, it replaces the entity from the database (as it is newer).
       *
       * @param dbQueryName The query name that needs to be executed.
       * @param parameter The parameters for the query.
       * @param entityMatcher The matcher used to determine which entities from the cache needs to be retained
       * @param checkCache If false, no cache check will be done, and the returned list will simply be the list from the database.
       */
      @SuppressWarnings("unchecked")
      protected List<EntityImpl> getList(
          String dbQueryName,
          Object parameter,
          CachedEntityMatcher<EntityImpl> cachedEntityMatcher,
          boolean checkCache
      ) {
          Collection<EntityImpl> result = getDbSqlSession().selectList(dbQueryName, parameter);
  
          if (checkCache) {
              Collection<CachedEntity> cachedObjects = getEntityCache().findInCacheAsCachedObjects(
                  getManagedEntityClass()
              );
  
              if ((cachedObjects != null && cachedObjects.size() > 0) || getManagedEntitySubClasses() != null) {
                  HashMap<String, EntityImpl> entityMap = new HashMap<String, EntityImpl>(result.size());
  
                  // Database entities
                  for (EntityImpl entity : result) {
                      entityMap.put(entity.getId(), entity);
                  }
  
                  // Cache entities
                  if (cachedObjects != null && cachedEntityMatcher != null) {
                      for (CachedEntity cachedObject : cachedObjects) {
                          EntityImpl cachedEntity = (EntityImpl) cachedObject.getEntity();
                          if (cachedEntityMatcher.isRetained(result, cachedObjects, cachedEntity, parameter)) {
                              entityMap.put(cachedEntity.getId(), cachedEntity); // will overwite db version with newer version
                          }
                      }
                  }
  
                  if (getManagedEntitySubClasses() != null && cachedEntityMatcher != null) {
                      for (Class<? extends EntityImpl> entitySubClass : getManagedEntitySubClasses()) {
                          Collection<CachedEntity> subclassCachedObjects = getEntityCache().findInCacheAsCachedObjects(
                              entitySubClass
                          );
                          if (subclassCachedObjects != null) {
                              for (CachedEntity subclassCachedObject : subclassCachedObjects) {
                                  EntityImpl cachedSubclassEntity = (EntityImpl) subclassCachedObject.getEntity();
                                  if (
                                      cachedEntityMatcher.isRetained(
                                          result,
                                          cachedObjects,
                                          cachedSubclassEntity,
                                          parameter
                                      )
                                  ) {
                                      entityMap.put(cachedSubclassEntity.getId(), cachedSubclassEntity); // will overwite db version with newer version
                                  }
                              }
                          }
                      }
                  }
  
                  result = entityMap.values();
              }
          }
  
          // Remove entries which are already deleted
          if (result.size() > 0) {
              Iterator<EntityImpl> resultIterator = result.iterator();
              while (resultIterator.hasNext()) {
                  if (getDbSqlSession().isEntityToBeDeleted(resultIterator.next())) {
                      resultIterator.remove();
                  }
              }
          }
  
          return new ArrayList<EntityImpl>(result);
      }
  
      protected List<EntityImpl> getListFromCache(CachedEntityMatcher<EntityImpl> entityMatcher, Object parameter) {
          Collection<CachedEntity> cachedObjects = getEntityCache().findInCacheAsCachedObjects(getManagedEntityClass());
  
          DbSqlSession dbSqlSession = getDbSqlSession();
  
          List<EntityImpl> result = new ArrayList<EntityImpl>(cachedObjects.size());
          if (cachedObjects != null && entityMatcher != null) {
              for (CachedEntity cachedObject : cachedObjects) {
                  EntityImpl cachedEntity = (EntityImpl) cachedObject.getEntity();
                  if (
                      entityMatcher.isRetained(null, cachedObjects, cachedEntity, parameter) &&
                      !dbSqlSession.isEntityToBeDeleted(cachedEntity)
                  ) {
                      result.add(cachedEntity);
                  }
              }
          }
  
          if (getManagedEntitySubClasses() != null && entityMatcher != null) {
              for (Class<? extends EntityImpl> entitySubClass : getManagedEntitySubClasses()) {
                  Collection<CachedEntity> subclassCachedObjects = getEntityCache().findInCacheAsCachedObjects(
                      entitySubClass
                  );
                  if (subclassCachedObjects != null) {
                      for (CachedEntity subclassCachedObject : subclassCachedObjects) {
                          EntityImpl cachedSubclassEntity = (EntityImpl) subclassCachedObject.getEntity();
                          if (
                              entityMatcher.isRetained(null, cachedObjects, cachedSubclassEntity, parameter) &&
                              !dbSqlSession.isEntityToBeDeleted(cachedSubclassEntity)
                          ) {
                              result.add(cachedSubclassEntity);
                          }
                      }
                  }
              }
          }
  
          return result;
      }
  }
  