in src/Azure.IIoT.OpcUa.Publisher/src/Parser/FilterModelBuilder.cs [500:657]
private FilterOperatorType GetFilterOperator(ParseTreeNode op, out bool negate,
out Func<List<FilterOperandModel>, string?> validator)
{
negate = false;
var opStr = op.FindTokenAndGetText();
FilterOperatorType filterOp;
switch (opStr.ToUpperInvariant())
{
case "NOT":
case "!":
filterOp = FilterOperatorType.Not;
validator = l => ValidateExact(1, l);
break;
case "=":
case "==":
filterOp = FilterOperatorType.Equals;
validator = l => ValidateExact(2, l);
break;
case ">":
filterOp = FilterOperatorType.GreaterThan;
validator = l => ValidateExact(2, l);
break;
case "<":
filterOp = FilterOperatorType.LessThan;
validator = l => ValidateExact(2, l);
break;
case ">=":
filterOp = FilterOperatorType.GreaterThan;
validator = l => ValidateExact(2, l);
break;
case "<=":
filterOp = FilterOperatorType.LessThanOrEqual;
validator = l => ValidateExact(2, l);
break;
case "LIKE":
filterOp = FilterOperatorType.Like;
validator = l => ValidateExact(2, l);
break;
case "<>":
case "!=":
negate = true;
filterOp = FilterOperatorType.Equals;
validator = l => ValidateExact(2, l);
break;
case "!<":
negate = true;
filterOp = FilterOperatorType.LessThan;
validator = l => ValidateExact(2, l);
break;
case "!>":
negate = true;
filterOp = FilterOperatorType.GreaterThan;
validator = l => ValidateExact(2, l);
break;
case "AND":
filterOp = FilterOperatorType.And;
validator = l => ValidateExact(2, l, true);
break;
case "OR":
filterOp = FilterOperatorType.Or;
validator = l => ValidateExact(2, l, true);
break;
case "CAST":
filterOp = FilterOperatorType.Cast;
validator = l => ValidateExact(2, l);
break;
case "&":
filterOp = FilterOperatorType.BitwiseAnd;
validator = l => ValidateExact(2, l);
break;
case "|":
filterOp = FilterOperatorType.BitwiseOr;
validator = l => ValidateExact(2, l);
break;
case "IN":
filterOp = FilterOperatorType.InList;
validator = l => ValidateMin(1, l);
break;
case "BETWEEN":
filterOp = FilterOperatorType.Between;
validator = l => ValidateExact(3, l);
break;
case "RELATEDTO":
filterOp = FilterOperatorType.RelatedTo;
validator = ValidateRelatedTo;
break;
case "ISNULL":
filterOp = FilterOperatorType.IsNull;
validator = l => ValidateExact(1, l);
break;
case "OFTYPE":
filterOp = FilterOperatorType.OfType;
validator = l => ValidateExact(1, l);
break;
case "INVIEW":
filterOp = FilterOperatorType.InView;
validator = l => ValidateExact(1, l);
break;
default:
throw ParserException.Create($"Operand {opStr} not supported",
_syntaxTree, op);
}
return filterOp;
//
// Generic expression validation. Validates the exact length of
// provided operands.
//
string? ValidateExact(int exact, List<FilterOperandModel> operands,
bool reverseOperands = false)
{
if (exact != operands.Count)
{
return $"Operator {opStr} ({filterOp}) requires {exact} " +
$"operand(s) but only {operands.Count} provided.";
}
if (reverseOperands && operands.All(o => o.Index != null))
{
//
// Call reverse on the operands to match the specification
// examples (validated in our tests)
//
operands.Reverse();
}
return null;
}
//
// Special case related to, which can have 3-6 operands
//
string? ValidateRelatedTo(IList<FilterOperandModel> operands)
{
if (operands.Count < 3)
{
return $"Operator {opStr} ({filterOp}) requires at least " +
$"3 operands, but {operands.Count} provided.";
}
if (operands.Count > 6)
{
return $"Operator {opStr} ({filterOp}) requires at most " +
$"6 operands, but {operands.Count} provided.";
}
return null;
}
//
// Validate minimum operands are provided.
//
string? ValidateMin(int min, IList<FilterOperandModel> operands)
{
if (operands.Count < min)
{
return $"Operator {opStr} ({filterOp}) requires at least " +
$"{min} operand(s), but {operands.Count} provided.";
}
return null;
}
}