/**
   * Get the number of readable resources fulfilling the provided detailed query. The detailed query
   * is a part of the overall query which allows to filter by single columns.
   *
   * @param pMetaDataManager The MetaDataManager used to query for the resources.
   * @param pDetailedQuery The detailed query, e.g. <i>name LIKE %John D%</i>
   * @param pContext The context used to authorize the access. Attention: User context needed.
   *     System context is not applicable here!
   * @return The number of readable resources.
   * @throws UnauthorizedAccessAttemptException If the context is not authorized to access the
   *     method or any resource.
   */
  @SecuredMethod(roleRequired = Role.GUEST)
  public final int getReadableResourceCount(
      IMetaDataManager pMetaDataManager,
      String pDetailedQuery,
      @Context IAuthorizationContext pContext)
      throws UnauthorizedAccessAttemptException {
    StringBuilder query = new StringBuilder();

    String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz);
    String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz);

    query.append("SELECT COUNT(f) FROM FilterHelper f,").append(tableName).append(" o WHERE ");

    query.append("f.userId='").append(pContext.getUserId().getStringRepresentation());
    query.append("' AND ");
    // add group information only if not admin context is used (role ADMINISTRATOR or group
    // SYS_ADMIN)
    if (!AuthorizationUtil.isAdminContext(pContext)) {
      query
          .append("f.groupId='")
          .append(pContext.getGroupId().getStringRepresentation())
          .append("' AND ");
    }

    query
        .append("f.domainId='")
        .append(domain)
        .append("' AND f.roleAllowed>=")
        .append(Role.GUEST.ordinal())
        .append(" AND f.domainUniqueId=o.")
        .append(uniqueField);

    if (pDetailedQuery != null) {
      query.append(" AND ").append(pDetailedQuery);
    }

    logger.debug("Executing query for readable resources: {}", query);
    return ((Number) pMetaDataManager.findSingleResult(query.toString())).intValue();
  }
  /**
   * Get a single readable resources with a known unique id fulfilling also the provided detailed
   * query. The detailed query is a part of the overall query which allows to filter by single
   * columns. This method is intended to be used for a fast query to check accessability for a known
   * resource.
   *
   * @param pMetaDataManager The MetaDataManager used to query for the resources.
   * @param pUniqueId The unique id of the resource.
   * @param pDetailedQuery The detailed query, e.g. <i>name LIKE %John D%</i>
   * @param pContext The context used to authorize the access. Attention: User context needed.
   *     System context is not applicable here!
   * @return A list of readable resources.
   * @throws UnauthorizedAccessAttemptException If the context is not authorized to access the
   *     method or any resource.
   */
  @SecuredMethod(roleRequired = Role.GUEST)
  public final C getReadableResource(
      IMetaDataManager pMetaDataManager,
      String pDetailedQuery,
      String pUniqueId,
      @Context IAuthorizationContext pContext)
      throws UnauthorizedAccessAttemptException {
    StringBuilder query = new StringBuilder();
    String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz);
    String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz);
    query.append("SELECT f.domainUniqueId FROM FilterHelper f WHERE ");
    query.append("f.userId='").append(pContext.getUserId().getStringRepresentation());
    query.append("' AND ");
    // add group information only if not admin context is used (role ADMINISTRATOR or group
    // SYS_ADMIN)
    if (!AuthorizationUtil.isAdminContext(pContext)) {
      query
          .append("f.groupId='")
          .append(pContext.getGroupId().getStringRepresentation())
          .append("' AND ");
    }
    query
        .append("f.domainId='")
        .append(domain)
        .append("' AND ")
        .append("f.domainUniqueId='")
        .append(pUniqueId)
        .append("' AND f.roleAllowed>=")
        .append(Role.GUEST.ordinal());

    Object fetchGraph =
        pMetaDataManager.getProperties().get(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH);
    pMetaDataManager.removeProperty(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH);
    logger.debug("Obtaining list of readable unique ids.");
    List<String> uniqueIds = pMetaDataManager.findResultList(query.toString(), String.class);
    if (fetchGraph != null) {
      logger.debug("Re-adding previously assigned fetch graph property.");
      pMetaDataManager.addProperty(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH, fetchGraph);
    }

    String uniqueId;
    if (uniqueIds.isEmpty()) {
      logger.warn("No domain unique ids found. Returning null.");
      return null;
    }

    if (uniqueIds.size() > 1) {
      logger.warn("Multiple domain unique ids found. Using first element {}", uniqueIds.get(0));
    }

    uniqueId = uniqueIds.get(0);
    query = new StringBuilder();
    query.append("SELECT o FROM ").append(tableName).append(" o WHERE ");
    query.append("o.").append(uniqueField).append("=?1");

    if (pDetailedQuery != null) {
      query.append(" AND ").append(pDetailedQuery);
    }

    C resutl = pMetaDataManager.findSingleResult(query.toString(), new Object[] {uniqueId}, clazz);
    return resutl;
    /*StringBuilder query = new StringBuilder();

    String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz);
    String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz);

    query.append("SELECT o FROM FilterHelper f, ").
    append(tableName).append(" o WHERE ");

    query.append("f.userId='").append(pContext.getUserId().getStringRepresentation());
    query.append("' AND ");

    query.append("f.groupId='").append(pContext.getGroupId().getStringRepresentation()).append("' AND ")
    .append("f.domainId='").append(domain).
    append("' AND f.roleAllowed>=").append(Role.GUEST.ordinal()).
    append(" AND f.domainUniqueId=o.").append(uniqueField);

    if (pDetailedQuery != null) {
    query.append(" AND ").append(pDetailedQuery);
    }

    logger.debug("Executing query for accessible resources: {}", query);
    return pMetaDataManager.findResultList(query.toString(), clazz, pFirstResult, pMaxResults);*/
  }
  /**
   * Get a list of readable resources fulfilling the provided detailed query. The detailed query is
   * a part of the overall query which allows to filter by single columns. The internal workflow
   * covers two queries: In a first query all resource ids of the appropriate resource type are
   * obtained. In a second query this list is used to query for the according resources. If the
   * number of requested results is larger than 1.000 or if a pDetailedQuery is not 'null', the
   * resource ids are slit into blocks of 1.000 elements and are used in subsequent queries as long
   * as the requested number of results is not reached. This is done for memory reasons. The result
   * list will start at index pFirstResult and will contain max. pMaxResults elements.
   *
   * <p>For databases holding a huge number of resources (40K+) or requested results (&gt;100) this
   * methods could have a poor performance. For fast checks of single resources, {@link
   * #getReadableResources(edu.kit.dama.mdm.core.IMetaDataManager, int, int,
   * edu.kit.dama.authorization.entities.IAuthorizationContext)} should be used.
   *
   * @param pMetaDataManager The MetaDataManager used to query for the resources.
   * @param pFirstResult The index of the first result.
   * @param pMaxResults The max. number of returned results.
   * @param pDetailedQuery The detailed query. The object queried for is adressed by 'o', e.g.
   *     <i>o.name LIKE %John D%</i>
   * @param pContext The context used to authorize the access. Attention: User context needed.
   *     System context is not applicable here!
   * @return A list of readable resources.
   * @throws UnauthorizedAccessAttemptException If the context is not authorized to access the
   *     method or any resource.
   */
  @SecuredMethod(roleRequired = Role.GUEST)
  public final List<C> getReadableResources(
      IMetaDataManager pMetaDataManager,
      String pDetailedQuery,
      int pFirstResult,
      int pMaxResults,
      @Context IAuthorizationContext pContext)
      throws UnauthorizedAccessAttemptException {
    StringBuilder query = new StringBuilder();
    String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz);
    String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz);
    logger.debug("Querying for domain unique ids of readable resources.");
    query.append("SELECT f.domainUniqueId FROM FilterHelper f WHERE ");
    query.append("f.userId='").append(pContext.getUserId().getStringRepresentation());
    query.append("' AND ");
    // add group information only if not admin context is used (role ADMINISTRATOR or group
    // SYS_ADMIN)
    if (!AuthorizationUtil.isAdminContext(pContext)) {
      query
          .append("f.groupId='")
          .append(pContext.getGroupId().getStringRepresentation())
          .append("' AND ");
    }
    query
        .append("f.domainId='")
        .append(domain)
        .append("' AND f.roleAllowed>=")
        .append(Role.GUEST.ordinal());

    logger.debug("Removing fetch graph property for FilterHelper query.");
    Object fetchGraph =
        pMetaDataManager.getProperties().get(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH);
    pMetaDataManager.removeProperty(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH);
    logger.debug("Obtaining list of readable unique ids.");

    // At this point ALL possible resource ids are fetched, which kills performance for large
    // repository instances, but is also necessary.
    // Reasons are: as later on the detailed query may filter out resources that are accessible in
    // principle.
    // In that case, additional resource ids would have to be loaded in order to be able to achieve
    // pMaxResults. The second reason is,
    // that single resources might be invalid, thus 100 resource ids might be retrieved but only 99
    // entities are returned which might confuse the user.
    List<String> uniqueIds = pMetaDataManager.findResultList(query.toString(), String.class);

    if (fetchGraph != null) {
      logger.debug("Re-adding previously assigned fetch graph property.");
      pMetaDataManager.addProperty(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH, fetchGraph);
    }

    if (uniqueIds.isEmpty()) {
      logger.warn("No domain unique ids found. Returning empty resources list.");
      return new ArrayList<>();
    }

    logger.info("{} domain unique id(s) found. Querying for resources.", uniqueIds.size());
    List<C> results = new ArrayList();
    // split query to subqueries
    int elementCount = uniqueIds.size();
    int startIndex = 0;
    // Read results in blocks...the max. block size is 1000 elements per query.
    // If the number of max. results is smaller than 1000 and no detailed query is provided,
    // the max. number of results is used as block size as this should already be the exact number
    // of required results. If a detailed query is provided, there is a probability of filtered
    // entries.
    // Therefor, the max. block size will be used to reduce the number of single database queries.
    // However, due to the check for the current number of results this will be the only query
    // if the first query returns 1000 results, pFirstResult is 0 and pMaxResults is LEQ 1000.
    int stepSize = (pMaxResults < 1000 && pDetailedQuery == null) ? pMaxResults : 1000;

    while (elementCount > 0) {
      List<String> subList =
          uniqueIds.subList(startIndex, Math.min(uniqueIds.size(), startIndex + stepSize));
      query = new StringBuilder();
      query.append("SELECT o FROM ").append(tableName).append(" o WHERE ");
      query.append("o.").append(uniqueField).append(" IN ?1");

      if (pDetailedQuery != null) {
        query.append(" AND ").append(pDetailedQuery);
      }
      List<C> subResults =
          pMetaDataManager.findResultList(query.toString(), new Object[] {subList}, clazz);
      results.addAll(subResults);
      if (results.size() >= pFirstResult + pMaxResults) {
        break;
      }
      elementCount -= subList.size();
      startIndex += subList.size();
    }

    return results.subList(pFirstResult, Math.min(results.size(), pFirstResult + pMaxResults));
    /*StringBuilder query = new StringBuilder();

    String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz);
    String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz);

    query.append("SELECT o FROM FilterHelper f, ").
    append(tableName).append(" o WHERE ");

    query.append("f.userId='").append(pContext.getUserId().getStringRepresentation());
    query.append("' AND ");

    query.append("f.groupId='").append(pContext.getGroupId().getStringRepresentation()).append("' AND ")
    .append("f.domainId='").append(domain).
    append("' AND f.roleAllowed>=").append(Role.GUEST.ordinal()).
    append(" AND f.domainUniqueId=o.").append(uniqueField);

    if (pDetailedQuery != null) {
    query.append(" AND ").append(pDetailedQuery);
    }

    logger.debug("Executing query for accessible resources: {}", query);
    return pMetaDataManager.findResultList(query.toString(), clazz, pFirstResult, pMaxResults);*/
  }