selectorName = new SelectorName(removeBracketsAndQuotes(first, position));
propertyName = parseName(tokens, typeSystem);
} else {
if (!(source instanceof Selector)) {
String msg = GraphI18n.functionIsAmbiguous.text("CONTAINS()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
selectorName = ((Selector)source).aliasOrName();
propertyName = removeBracketsAndQuotes(first, position);
}
tokens.consume(',');
if (tokens.canConsume('$')) {
// The value parameter is a bind variable ...
BindVariableName var = parseBindVariableName(tokens, typeSystem);
try {
constraint = fullTextSearch(selectorName, propertyName, var);
} catch (RepositoryException e) {
String msg = GraphI18n.functionHasInvalidBindVariable.text("CONTAINS()", pos.getLine(), pos.getColumn(), var);
throw new ParsingException(pos, msg);
}
} else {
// It's just a full text search expression (don't remove nested quotes!!!) ...
String expression = removeBracketsAndQuotes(tokens.consume(), false, tokens.previousPosition());
Term term = parseFullTextSearchExpression(expression, tokens.previousPosition());
constraint = fullTextSearch(selectorName, propertyName, expression, term);
}
tokens.consume(")");
} else if (tokens.canConsume("ISSAMENODE", "(")) {
SelectorName selectorName = null;
if (tokens.matches(ANY_VALUE, ")")) {
if (!(source instanceof Selector)) {
String msg = GraphI18n.functionIsAmbiguous.text("ISSAMENODE()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
selectorName = ((Selector)source).name();
} else {
selectorName = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
tokens.consume(')');
constraint = sameNode(selectorName, path);
} else if (tokens.canConsume("ISCHILDNODE", "(")) {
SelectorName selectorName = null;
if (tokens.matches(ANY_VALUE, ")")) {
if (!(source instanceof Selector)) {
String msg = GraphI18n.functionIsAmbiguous.text("ISCHILDNODE()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
selectorName = ((Selector)source).name();
} else {
selectorName = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
tokens.consume(')');
constraint = childNode(selectorName, path);
} else if (tokens.canConsume("ISDESCENDANTNODE", "(")) {
SelectorName selectorName = null;
if (tokens.matches(ANY_VALUE, ")")) {
if (!(source instanceof Selector)) {
String msg = GraphI18n.functionIsAmbiguous.text("ISDESCENDANTNODE()", pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
selectorName = ((Selector)source).name();
} else {
selectorName = parseSelectorName(tokens, typeSystem);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
tokens.consume(')');
constraint = descendantNode(selectorName, path);
} else if (tokens.canConsume("RELIKE", "(")) {
StaticOperand left = parseStaticOperand(tokens, typeSystem);
tokens.consume(',');
PropertyValue right = parsePropertyValue(tokens, typeSystem, source);
tokens.consume(')');
constraint = new Relike(left, right);
} else {
// First try a property existance ...
Position pos2 = tokens.nextPosition();
constraint = parsePropertyExistance(tokens, typeSystem, source);
if (constraint == null) {
// Try to parse as a dynamic operand ...
DynamicOperand left = parseDynamicOperand(tokens, typeSystem, source);
if (left != null) {
if (tokens.matches('(') && left instanceof PropertyValue) {
// This was probably a bad function that we parsed as the start of a dynamic operation ...
String name = ((PropertyValue)left).getPropertyName(); // this may be the function name
String msg = GraphI18n.expectingConstraintCondition.text(name, pos2.getLine(), pos2.getColumn());
throw new ParsingException(pos, msg);
}
if (tokens.matches("IN", "(") || tokens.matches("NOT", "IN", "(")) {
boolean not = tokens.canConsume("NOT");
Collection<StaticOperand> staticOperands = parseInClause(tokens, typeSystem);
constraint = setCriteria(left, staticOperands);
if (not) constraint = not(constraint);
} else if (tokens.matches("BETWEEN") || tokens.matches("NOT", "BETWEEN")) {
boolean not = tokens.canConsume("NOT");
tokens.consume("BETWEEN");
StaticOperand lowerBound = parseStaticOperand(tokens, typeSystem);
boolean lowerInclusive = !tokens.canConsume("EXCLUSIVE");
tokens.consume("AND");
StaticOperand upperBound = parseStaticOperand(tokens, typeSystem);
boolean upperInclusive = !tokens.canConsume("EXCLUSIVE");
constraint = between(left, lowerBound, upperBound, lowerInclusive, upperInclusive);
if (not) constraint = not(constraint);
} else if (tokens.matches("NOT", "LIKE")) {
tokens.consume("NOT");
Operator operator = parseComparisonOperator(tokens);
StaticOperand right = parseStaticOperand(tokens, typeSystem);
constraint = comparison(left, operator, right);
constraint = not(constraint);
} else {
Operator operator = parseComparisonOperator(tokens);
StaticOperand right = parseStaticOperand(tokens, typeSystem);
constraint = comparison(left, operator, right);
}
}
// else continue ...
}
}
if (constraint == null) {
String msg = GraphI18n.expectingConstraintCondition.text(tokens.consume(), pos.getLine(), pos.getColumn());
throw new ParsingException(pos, msg);
}
// 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 = and(constraint, rhs);