/**
   * Uso do filtro:<br>
   * Filtrando valores indesejados: filtro.put("!campo", "valor");<br>
   * Por padrão, esse método procura campos string usando MatchMode.ANYWHERE,<br>
   * se quiser usar MatchMode.EXACT você deve usar "==" <br>
   * no início do nome do campo: filtro.put("==campo", "valor");<br>
   * Você pode usar a "!" e "==", ao mesmo tempo: filtro.put("!==campo", "valor");
   *
   * @param filtro Filtro para consulta.
   * @return Resultado paginado com lista de T e quantidade total de registros.
   */
  @SuppressWarnings(UNCHECKED)
  public ResultadoPaginado<T> consultarPorFiltro(Filtro filtro) {

    Criteria quantidadeCriteria = createCriteria(getClassePersistente());
    adicionarCampos(filtro, quantidadeCriteria);
    int quantidade = getTotalRegistros(quantidadeCriteria).intValue();

    List<T> resultado = new ArrayList<T>();
    if (quantidade > 0) {
      Criteria criteria = createCriteria(getClassePersistente());
      Set<String> aliases = adicionarCampos(filtro, criteria);
      adicionarOrdenacao(criteria, filtro.getOrdem(), aliases);
      criteria.setFirstResult(filtro.getPrimeiroElemento());
      criteria.setMaxResults(filtro.getQuantidadeMaximaResultados());
      resultado = criteria.list();
    }

    return new ResultadoPaginado<T>(resultado, quantidade);
  }
  protected Set<String> adicionarCampos(Filtro filter, Criteria criteria) {
    Set<String> aliases = new HashSet<String>();

    for (Entry<String, Object> entry : filter.entrySet()) {
      String atributo = entry.getKey();

      Boolean isNegacao = Boolean.FALSE;
      Boolean isValorExato = Boolean.FALSE;

      // Nega a verificação
      if (atributo.charAt(0) == '!') {
        isNegacao = Boolean.TRUE;
        atributo = atributo.substring(1);
      }

      // Verifica se a restrição deve ser exata
      if (atributo.startsWith("==")) {
        isValorExato = Boolean.TRUE;
        atributo = atributo.substring(2);
      }

      Object valor = entry.getValue();

      if (isNaoVazio(valor)) {

        // Preparando as alias
        String alias = getAlias(atributo);

        if (StringUtils.isNotBlank(alias) && !aliases.contains(alias)) {
          aliases.add(alias);
          criteria.createAlias(alias, alias);
        }

        // Adicionando restrição
        Criterion criterion = null;

        if (valor.equals(Filtro.VALOR_NULO)) {
          criterion = Restrictions.isNull(atributo);
        } else if (valor.equals(Filtro.VALOR_NAO_NULO)) {
          criterion = Restrictions.isNotNull(atributo);
        } else if (valor instanceof String) {

          // Verifica se a restrição de string é exata
          if (isValorExato) {
            criterion =
                Restrictions.ilike(atributo, ((String) valor).toLowerCase(), MatchMode.EXACT);
          } else {
            // Se não, consulta em qualquer posição
            criterion =
                Restrictions.ilike(atributo, ((String) valor).toLowerCase(), MatchMode.ANYWHERE);
          }

        } else if (valor instanceof Collection) {
          criterion = Restrictions.in(atributo, (Collection<?>) valor);
        } else {
          criterion = Restrictions.eq(atributo, valor);
        }

        // Caso negação
        if (isNegacao) {
          criterion = Restrictions.not(criterion);
        }
        criteria.add(criterion);
      }
    }
    return aliases;
  }