private void buildSelectClause(
      StringBuilder queryStringBuilder,
      CriteriaQueryTranslator criteriaQueryTranslator,
      Set<String> associationPaths) {
    // "distinct" is needed only for association criteria, but it prevents specifying ordering by
    // "ace.id"
    if (associationPaths.isEmpty()) {
      queryStringBuilder.append("select ");
    } else {
      queryStringBuilder.append("select distinct ");
    }
    queryStringBuilder.append(criteriaQueryTranslator.getRootSQLALias());
    queryStringBuilder.append(" from AccessControlEntry ace, ");
    queryStringBuilder.append(persistentClass.getSimpleName());
    queryStringBuilder.append(' ');
    queryStringBuilder.append(criteriaQueryTranslator.getRootSQLALias());

    for (String associationPath : associationPaths) {
      queryStringBuilder.append(" join ");
      queryStringBuilder.append(criteriaQueryTranslator.getRootSQLALias());
      queryStringBuilder.append('.');
      queryStringBuilder.append(associationPath);
      queryStringBuilder.append(" as ");
      queryStringBuilder.append(
          criteriaQueryTranslator.getSQLAlias(
              criteriaQueryTranslator.getCriteria(associationPath)));
    }
  }
  private List<TypedValue> buildWhereClause(
      StringBuilder queryStringBuilder,
      QueryModel queryModel,
      Criteria criteria,
      CriteriaQueryTranslator criteriaQueryTranslator) {
    List<TypedValue> parameters = new ArrayList<TypedValue>();

    queryStringBuilder.append(" where ");

    if (queryModel.getConditions() != null) {
      for (Condition condition : queryModel.getConditions()) {
        Criterion criterion = (Criterion) condition.getConstraint();

        queryStringBuilder.append(criterion.toSqlString(criteria, criteriaQueryTranslator));
        queryStringBuilder.append(" and ");

        parameters.addAll(
            Arrays.asList(criterion.getTypedValues(criteria, criteriaQueryTranslator)));
      }
    }

    if (queryModel.getAssociationConditions() != null) {
      for (Map.Entry<String, List<Condition>> associationCriteriaEntry :
          queryModel.getAssociationConditions().entrySet()) {
        CriteriaImpl.Subcriteria associationCriteria =
            (CriteriaImpl.Subcriteria)
                criteriaQueryTranslator.getCriteria(associationCriteriaEntry.getKey());

        for (Condition condition : associationCriteriaEntry.getValue()) {
          Criterion criterion = (Criterion) condition.getConstraint();

          queryStringBuilder.append(
              criterion.toSqlString(associationCriteria, criteriaQueryTranslator));
          queryStringBuilder.append(" and ");

          parameters.addAll(
              Arrays.asList(
                  criterion.getTypedValues(associationCriteria, criteriaQueryTranslator)));
        }
      }
    }

    queryStringBuilder.append(" ace.objectType = '");
    queryStringBuilder.append(persistentClass.getSimpleName());
    queryStringBuilder.append("' and ace.objectId = ");
    queryStringBuilder.append(criteriaQueryTranslator.getRootSQLALias());
    queryStringBuilder.append('.');
    queryStringBuilder.append(idField);
    queryStringBuilder.append(" and ace.accessibleBy = ? ");

    return parameters;
  }
  protected String generateTableAlias(int n, PropertyPath path, Joinable joinable) {
    // TODO: deal with side-effects (changes to includeInResultRowList, userAliasList,
    // resultTypeList)!!!

    // for collection-of-entity, we are called twice for given "path"
    // once for the collection Joinable, once for the entity Joinable.
    // the second call will/must "consume" the alias + perform side effects according to
    // consumesEntityAlias()
    // for collection-of-other, however, there is only one call
    // it must "consume" the alias + perform side effects, despite what consumeEntityAlias() return
    // says
    //
    // note: the logic for adding to the userAliasList is still strictly based on
    // consumesEntityAlias return value
    boolean checkForSqlAlias = joinable.consumesEntityAlias();

    if (!checkForSqlAlias && joinable.isCollection()) {
      // is it a collection-of-other (component or value) ?
      CollectionPersister collectionPersister = (CollectionPersister) joinable;
      Type elementType = collectionPersister.getElementType();
      if (elementType.isComponentType() || !elementType.isEntityType()) {
        checkForSqlAlias = true;
      }
    }

    String sqlAlias = null;

    if (checkForSqlAlias) {
      final Criteria subcriteria = translator.getCriteria(path.getFullPath());
      sqlAlias = subcriteria == null ? null : translator.getSQLAlias(subcriteria);

      if (joinable.consumesEntityAlias() && !translator.hasProjection()) {
        includeInResultRowList.add(subcriteria != null && subcriteria.getAlias() != null);
        if (sqlAlias != null) {
          if (subcriteria.getAlias() != null) {
            userAliasList.add(subcriteria.getAlias());
            resultTypeList.add(translator.getResultType(subcriteria));
          }
        }
      }
    }

    if (sqlAlias == null) {
      sqlAlias = super.generateTableAlias(n + translator.getSQLAliasCount(), path, joinable);
    }

    return sqlAlias;
  }
  public CriteriaJoinWalker(
      final OuterJoinLoadable persister,
      final CriteriaQueryTranslator translator,
      final SessionFactoryImplementor factory,
      final CriteriaImpl criteria,
      final String rootEntityName,
      final LoadQueryInfluencers loadQueryInfluencers,
      final String alias) {
    super(persister, factory, loadQueryInfluencers, alias);

    this.translator = translator;

    querySpaces = translator.getQuerySpaces();

    if (translator.hasProjection()) {
      initProjection(
          translator.getSelect(),
          translator.getWhereCondition(),
          translator.getOrderBy(),
          translator.getGroupBy(),
          LockOptions.NONE);
      resultTypes = translator.getProjectedTypes();
      userAliases = translator.getProjectedAliases();
      includeInResultRow = new boolean[resultTypes.length];
      Arrays.fill(includeInResultRow, true);
    } else {
      initAll(translator.getWhereCondition(), translator.getOrderBy(), LockOptions.NONE);
      // root entity comes last
      userAliasList.add(criteria.getAlias()); // root entity comes *last*
      resultTypeList.add(translator.getResultType(criteria));
      includeInResultRowList.add(true);
      userAliases = ArrayHelper.toStringArray(userAliasList);
      resultTypes = ArrayHelper.toTypeArray(resultTypeList);
      includeInResultRow = ArrayHelper.toBooleanArray(includeInResultRowList);
    }
  }
 protected boolean hasRestriction(PropertyPath path) {
   return translator.hasRestriction(path.getFullPath());
 }
 protected String getWithClause(PropertyPath path) {
   return translator.getWithClause(path.getFullPath());
 }
 protected JoinType getJoinType(
     OuterJoinLoadable persister,
     final PropertyPath path,
     int propertyNumber,
     AssociationType associationType,
     FetchMode metadataFetchMode,
     CascadeStyle metadataCascadeStyle,
     String lhsTable,
     String[] lhsColumns,
     final boolean nullable,
     final int currentDepth)
     throws MappingException {
   final JoinType resolvedJoinType;
   if (translator.isJoin(path.getFullPath())) {
     resolvedJoinType = translator.getJoinType(path.getFullPath());
   } else {
     if (translator.hasProjection()) {
       resolvedJoinType = JoinType.NONE;
     } else {
       FetchMode fetchMode = translator.getRootCriteria().getFetchMode(path.getFullPath());
       if (isDefaultFetchMode(fetchMode)) {
         if (persister != null) {
           if (isJoinFetchEnabledByProfile(persister, path, propertyNumber)) {
             if (isDuplicateAssociation(lhsTable, lhsColumns, associationType)) {
               resolvedJoinType = JoinType.NONE;
             } else if (isTooDeep(currentDepth)
                 || (associationType.isCollectionType() && isTooManyCollections())) {
               resolvedJoinType = JoinType.NONE;
             } else {
               resolvedJoinType = getJoinType(nullable, currentDepth);
             }
           } else {
             resolvedJoinType =
                 super.getJoinType(
                     persister,
                     path,
                     propertyNumber,
                     associationType,
                     metadataFetchMode,
                     metadataCascadeStyle,
                     lhsTable,
                     lhsColumns,
                     nullable,
                     currentDepth);
           }
         } else {
           resolvedJoinType =
               super.getJoinType(
                   associationType,
                   metadataFetchMode,
                   path,
                   lhsTable,
                   lhsColumns,
                   nullable,
                   currentDepth,
                   metadataCascadeStyle);
         }
       } else {
         if (fetchMode == FetchMode.JOIN) {
           isDuplicateAssociation(
               lhsTable, lhsColumns, associationType); // deliberately ignore return value!
           resolvedJoinType = getJoinType(nullable, currentDepth);
         } else {
           resolvedJoinType = JoinType.NONE;
         }
       }
     }
   }
   return resolvedJoinType;
 }