docs: public static <T> Predicate buildQueryConditions(final Filter filter, final IFilterQueryBuilder<T> filterQueryBuilder) {
          if (filterQueryBuilder == null) return null;
  
          // Reset the query builder to ensure it hasn't got any conditions set
          filterQueryBuilder.reset();
  
          // the categories to be ANDed will be added to this string
          final List<Predicate> andQueryBlock = new ArrayList<Predicate>();
          // the categories to be ORed will be added to this string
          final List<Predicate> orQueryBlock = new ArrayList<Predicate>();
  
          final CriteriaBuilder queryBuilder = filterQueryBuilder.getCriteriaBuilder();
          final List<Predicate> whereQuery = new ArrayList<Predicate>();
  
          /* Check if the Query Builder can build using Tags & Categories */
          if (filterQueryBuilder instanceof ITagFilterQueryBuilder) {
              final ITagFilterQueryBuilder tagFilterQueryBuilder = (ITagFilterQueryBuilder) filterQueryBuilder;
  
              // loop over the projects that the tags in this filter are assigned to
              for (final Project project : filter.getFilterTagProjects()) {
                  // loop over the categories that the tags in this filter are
                  // assigned to
                  for (final Category category : filter.getFilterTagCategories()) {
                      // define the default logic used for the FilterTag categories
                      String catInternalLogic = CommonFilterConstants.OR_LOGIC;
                      String catExternalLogic = CommonFilterConstants.AND_LOGIC;
      
                      /*
                       * Now loop over the FilterCategories, looking for any
                       * categories that specify a particular boolean logic to apply.
                       * Remember that not all FilterTags will have an associated
                       * FilterCategory that specifies the logic to use, in which case
                       * the default logic defined above will be used.
                       */
                      final Set<FilterCategory> filterCategories = filter.getFilterCategories();
                      for (final FilterCategory filterCategory : filterCategories) {
                          final boolean categoryMatch = category.equals(filterCategory.getCategory());
                          /*
                           * project or filterCatgeory.getProject() might be null.
                           * CollectionUtilities.isEqual deals with this situation
                           */
                          final boolean projectMatch = CollectionUtilities.isEqual(project, filterCategory.getProject());
  
                          if (categoryMatch && projectMatch) {
                              final int categoryState = filterCategory.getCategoryState();
  
                              if (categoryState == CommonFilterConstants.CATEGORY_INTERNAL_AND_STATE)
                                  catInternalLogic = CommonFilterConstants.AND_LOGIC;
                              else if (categoryState == CommonFilterConstants.CATEGORY_INTERNAL_OR_STATE)
                                  catInternalLogic = CommonFilterConstants.OR_LOGIC;
                              else if (categoryState == CommonFilterConstants.CATEGORY_EXTERNAL_AND_STATE)
                                  catExternalLogic = CommonFilterConstants.AND_LOGIC;
                              else if (categoryState == CommonFilterConstants.CATEGORY_EXTERNAL_OR_STATE)
                                  catExternalLogic = CommonFilterConstants.OR_LOGIC;
                          }
                      }
      
                      /*
                       * now build up the HQL that checks to see if the FilterTags
                       * exist (or not) in this category
                       */
                      final List<Predicate> categoryBlocks = new ArrayList<Predicate>();
  
                      boolean matchedSomeTags = false;
  
                      final Set<FilterTag> filterTags = filter.getFilterTags();
                      for (final FilterTag filterTag : filterTags) {
                          final Tag tag = filterTag.getTag();
      
                          /*
                           * first check to make sure that the FilterTag is actually
                           * associated with the category we are looking at now
                           */
                          if (tag.isInCategory(category) && tag.isInProject(project)) {
                              /*
                               * a FilterTag state of 1 "means exists in category",
                               * and 0 means "does not exist in category"
                               */
                              final boolean matchTag = filterTag.getTagState() == CommonFilterConstants.MATCH_TAG_STATE;
                              final boolean notMatchTag = filterTag.getTagState() == CommonFilterConstants.NOT_MATCH_TAG_STATE;
  
                              if (matchTag || notMatchTag) {
                                  matchedSomeTags = true;
  
                                  if (matchTag) {
                                      /* match the tag in this category */
                                      categoryBlocks.add(tagFilterQueryBuilder.getMatchTagString(tag.getTagId()));
                                  } else if (notMatchTag) {
                                      /*
                                       * make sure this tag does not exist in this
                                       * category
                                       */
                                      categoryBlocks.add(tagFilterQueryBuilder.getNotMatchTagString(tag.getTagId()));
                                  }
                              }
                          }
                      }
  
                      if (matchedSomeTags) {
                          final Predicate categoryBlock;
                          if (categoryBlocks.size() > 1) {
                              final Predicate[] catBlockArray = categoryBlocks.toArray(new Predicate[categoryBlocks.size()]);
                              if (catInternalLogic.equalsIgnoreCase(CommonFilterConstants.OR_LOGIC)) {
                                  categoryBlock = queryBuilder.or(catBlockArray);
                              } else {
                                  categoryBlock = queryBuilder.and(catBlockArray);
                              }
                          } else {
                              categoryBlock = categoryBlocks.get(0);
                          }
  
                          // append this clause to the appropriate block
                          if (catExternalLogic.equalsIgnoreCase(CommonFilterConstants.AND_LOGIC)) {
                              andQueryBlock.add(categoryBlock);
                          } else {
                              orQueryBlock.add(categoryBlock);
                          }
                      }
  
                  }
              }
          }
          
          /* Check if the Query Builder can build using Locales */
          if (filterQueryBuilder instanceof ILocaleFilterQueryBuilder) {
              final ILocaleFilterQueryBuilder localeFilterQueryBuilder = (ILocaleFilterQueryBuilder) filterQueryBuilder;
              
              /*
               * Loop over the locales and add the query
               */
              final List<Predicate> localeBlock = new ArrayList<Predicate>();
              final List<Predicate> notLocaleBlock = new ArrayList<Predicate>();
              for (final FilterLocale filterLocale : filter.getFilterLocales()) {
                  if (filterLocale.getLocaleState() == CommonFilterConstants.MATCH_TAG_STATE) {
                      localeBlock.add(localeFilterQueryBuilder.getMatchingLocaleString(filterLocale.getLocaleName()));
                  } else if (filterLocale.getLocaleState() == CommonFilterConstants.NOT_MATCH_TAG_STATE) {
                      notLocaleBlock.add(localeFilterQueryBuilder.getNotMatchingLocaleString(filterLocale.getLocaleName()));
                  }
              }
  
              if (!localeBlock.isEmpty() || !notLocaleBlock.isEmpty()) {
  
                  Predicate localeBlockPredicate = null;
                  if (localeBlock.size() > 1) {
                      final Predicate[] predicateArray = localeBlock.toArray(new Predicate[localeBlock.size()]);
                      localeBlockPredicate = queryBuilder.or(predicateArray);
                  } else if (localeBlock.size() == 1) {
                      localeBlockPredicate = localeBlock.get(0);
                  }
  
                  Predicate notLocaleBlockPredicate = null;
                  if (notLocaleBlock.size() > 1) {
                      final Predicate[] predicateArray = notLocaleBlock.toArray(new Predicate[notLocaleBlock.size()]);
                      notLocaleBlockPredicate = queryBuilder.and(predicateArray);
                  } else if (notLocaleBlock.size() == 1) {
                      notLocaleBlockPredicate = notLocaleBlock.get(0);
                  }
  
                  if (localeBlockPredicate != null && notLocaleBlockPredicate != null)
                      andQueryBlock.add(queryBuilder.and(localeBlockPredicate, notLocaleBlockPredicate));
                  else if (localeBlockPredicate != null) andQueryBlock.add(localeBlockPredicate);
                  else andQueryBlock.add(notLocaleBlockPredicate);
              }
          }
  
          /*
           * build up the category query if some conditions were specified if not,
           * we will just return an empty string
           */
          if (!andQueryBlock.isEmpty() || !orQueryBlock.isEmpty()) {
              // add the and categories
              Predicate andQueryPredicate = null;
              if (andQueryBlock.size() > 1) {
                  final Predicate[] predicateArray = andQueryBlock.toArray(new Predicate[andQueryBlock.size()]);
                  andQueryPredicate = queryBuilder.and(predicateArray);
              } else if (andQueryBlock.size() == 1) {
                  andQueryPredicate = andQueryBlock.get(0);
              }
  
              // add the or categories
              Predicate orQueryPredicate = null;
              if (orQueryBlock.size() > 1) {
                  final Predicate[] predicateArray = orQueryBlock.toArray(new Predicate[orQueryBlock.size()]);
                  orQueryPredicate = queryBuilder.or(predicateArray);
              } else if (orQueryBlock.size() == 1) {
                  orQueryPredicate = orQueryBlock.get(0);
              }
  
              if (andQueryPredicate != null && orQueryPredicate != null) whereQuery.add(queryBuilder.or(andQueryPredicate, orQueryPredicate));
              else if (andQueryPredicate != null) whereQuery.add(andQueryPredicate);
              else whereQuery.add(orQueryPredicate);
          }
          
          /*
           * Do an initial loop over the FilterFields, looking for the field logic
           * value
           */
          for (final FilterField filterField : filter.getFilterFields())
              filterQueryBuilder.addFilterField(filterField.getField(), filterField.getValue());
  
          filterQueryBuilder.process();
          final Predicate fieldQuery = filterQueryBuilder.getFilterConditions();
          if (fieldQuery != null) {
              whereQuery.add(fieldQuery);
          }
  
          if (whereQuery.size() > 1) {
              final Predicate[] queries = whereQuery.toArray(new Predicate[whereQuery.size()]);
              return queryBuilder.and(queries);
          } else if (whereQuery.size() == 1) {
              return whereQuery.get(0);
          }
  
          return null;
      }