selectorName = new SelectorName(removeBracketsAndQuotes(first));
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).getName();
propertyName = first;
}
tokens.consume(',');
// Followed by the full text search expression ...
String expression = removeBracketsAndQuotes(tokens.consume());
Term term = parseFullTextSearchExpression(expression, tokens.previousPosition());
tokens.consume(")");
constraint = new FullTextSearch(selectorName, propertyName, expression, term);
} 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).getName();
} else {
selectorName = parseSelectorName(tokens);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
tokens.consume(')');
constraint = new 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).getName();
} else {
selectorName = parseSelectorName(tokens);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
tokens.consume(')');
constraint = new 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).getName();
} else {
selectorName = parseSelectorName(tokens);
tokens.consume(',');
}
String path = parsePath(tokens, typeSystem);
tokens.consume(')');
constraint = new DescendantNode(selectorName, path);
} 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 = new SetCriteria(left, staticOperands);
if (not) constraint = new 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 = new Between(left, lowerBound, upperBound, lowerInclusive, upperInclusive);
if (not) constraint = new Not(constraint);
} else {
Operator operator = parseComparisonOperator(tokens);
StaticOperand right = parseStaticOperand(tokens, typeSystem);
constraint = new 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 = new And(constraint, parseConstraint(tokens, typeSystem, source));
}