private void visitRange(ConjunctionPredicate and) {
   if (and.getPredicates().size() != 2) {
     throw new IllegalArgumentException("no valid range");
   }
   GreaterThanOrEqualsPredicate lower = null;
   LessThanOrEqualsPredicate upper = null;
   for (Predicate p : and.getPredicates()) {
     if (p instanceof GreaterThanOrEqualsPredicate) {
       lower = (GreaterThanOrEqualsPredicate) p;
     } else if (p instanceof LessThanOrEqualsPredicate) {
       upper = (LessThanOrEqualsPredicate) p;
     }
   }
   if (lower == null || upper == null || lower.getKey() != upper.getKey()) {
     throw new IllegalArgumentException("no valid range");
   }
   addParamValue(lower.getKey(), "", lower.getValue() + "-" + upper.getValue());
 }
  private void visit(ConjunctionPredicate and) throws IllegalStateException {
    // ranges are allowed underneath root - try first
    try {
      visitRange(and);
      return;
    } catch (IllegalArgumentException e) {
      // must be a root AND
    }

    if (state != State.ROOT) {
      throw new IllegalStateException("AND must be a root predicate or a valid range");
    }
    state = State.AND;

    for (Predicate p : and.getPredicates()) {
      lastParam = null;
      visit(p);
    }
    state = State.ROOT;
  }