docs: @Override
      protected Constraint parseConstraint( TokenStream tokens,
                                            TypeSystem typeSystem,
                                            Source source ) {
          Constraint constraint = null;
          if (tokens.canConsume("JCR", ":", "PATH")) {
              // It is a property constraint on "jcr:path" ...
              SelectorName selector = getSelectorNameFor(source);
              PropertyValue value = new PropertyValue(selector, "jcr:path");
              Operator operator = parseComparisonOperator(tokens);
              StaticOperand right = parseStaticOperand(tokens, typeSystem);
              constraint = rewriteConstraint(new Comparison(value, operator, right));
          } else if (tokens.matches(ANY_VALUE, "IN")) {
              // This is a "... 'value' IN prop ..." pattern used in the JCR TCK tests but not in the JCR 1.0.1 specification
              // ...
              Literal value = parseLiteral(tokens, typeSystem);
              tokens.consume("IN");
              PropertyValue propertyValue = parsePropertyValue(tokens, typeSystem, source);
              constraint = new SetCriteria(propertyValue, value);
          } else if (source instanceof JoinableSources
                     && !(tokens.matches("(") || tokens.matches("NOT") || tokens.matches("CONTAINS", "(")
                          || tokens.matches("ISSAMENODE", "(") || tokens.matches("ISCHILDNODE", "(") || tokens.matches("ISDESCENDANTNODE",
                                                                                                                       "("))) {
              JoinableSources joinableSources = (JoinableSources)source;
              // See if this is a join condition ...
              if (tokens.matches(ANY_VALUE, ":", ANY_VALUE, ".", "JCR", ":", "PATH", "=")
                  || tokens.matches(ANY_VALUE, ".", "JCR", ":", "PATH", "=")) {
                  Position position = tokens.nextPosition();
                  SelectorName selector1 = parseSelectorName(tokens, typeSystem);
                  tokens.consume('.');
                  parseName(tokens, typeSystem); // jcr:path
                  tokens.consume('=');
                  SelectorName selector2 = parseSelectorName(tokens, typeSystem);
                  tokens.consume('.');
                  parseName(tokens, typeSystem); // jcr:path
                  joinableSources.add(new SameNodeJoinCondition(selector1, selector2), position);
  
                  // AND has higher precedence than OR, so we need to evaluate it first ...
                  while (tokens.canConsume("AND")) {
                      Constraint rhs = parseConstraint(tokens, typeSystem, source);
                      if (rhs != null) constraint = constraint != null ? new And(constraint, rhs) : rhs;
                  }
                  while (tokens.canConsume("OR")) {
                      Constraint rhs = parseConstraint(tokens, typeSystem, source);
                      if (rhs != null) constraint = constraint != null ? new And(constraint, rhs) : rhs;
                  }
                  return constraint;
              }
          }
          if (constraint != null) {
              // AND has higher precedence than OR, so we need to evaluate it first ...
              while (tokens.canConsume("AND")) {
                  Constraint rhs = parseConstraint(tokens, typeSystem, source);
                  if (rhs != null) constraint = new And(constraint, rhs);
              }
              while (tokens.canConsume("OR")) {
                  Constraint rhs = parseConstraint(tokens, typeSystem, source);
                  if (rhs != null) constraint = new Or(constraint, rhs);
              }
              return constraint;
          }
  
          constraint = super.parseConstraint(tokens, typeSystem, source);
          constraint = rewriteConstraint(constraint);
          return constraint;
      }