/// <summary> /// Tries to create an XPath Function expression if the function Uri correseponds to a supported XPath Function /// </summary> /// <param name="u">Function Uri</param> /// <param name="args">Function Arguments</param> /// <param name="scalarArgs">Scalar Arguments</param> /// <param name="expr">Generated Expression</param> /// <returns>Whether an expression was successfully generated</returns> public bool TryCreateExpression(Uri u, List<ISparqlExpression> args, Dictionary<String,ISparqlExpression> scalarArgs, out ISparqlExpression expr) { //If any Scalar Arguments are present then can't possibly be an XPath Function if (scalarArgs.Count > 0) { expr = null; return false; } String func = u.ToString(); if (func.StartsWith(XPathFunctionFactory.XPathFunctionsNamespace)) { func = func.Substring(XPathFunctionFactory.XPathFunctionsNamespace.Length); ISparqlExpression xpathFunc = null; switch (func) { case XPathFunctionFactory.Absolute: if (args.Count == 1) { xpathFunc = new XPathAbsoluteFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath abs() function"); } break; case XPathFunctionFactory.AdjustDateTimeToTimezone: throw new NotSupportedException("XPath adjust-dateTime-to-timezone() function is not supported"); case XPathFunctionFactory.Boolean: if (args.Count == 1) { xpathFunc = new XPathBooleanFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath boolean() function"); } throw new NotSupportedException("XPath boolean() function is not supported"); case XPathFunctionFactory.Ceiling: if (args.Count == 1) { xpathFunc = new XPathCeilingFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath ceiling() function"); } break; case XPathFunctionFactory.Compare: if (args.Count == 2) { xpathFunc = new XPathCompareFunction(args.First(), args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath compare() function"); } break; case XPathFunctionFactory.Concat: if (args.Count == 2) { xpathFunc = new XPathConcatFunction(args.First(), args.Last()); } else if (args.Count > 2) { xpathFunc = new XPathConcatFunction(args); } else { throw new RdfParseException("Incorrect number of arguments for the XPath concat() function"); } break; case XPathFunctionFactory.Contains: if (args.Count == 2) { xpathFunc = new XPathContainsFunction(args.First(), args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath contains() function"); } break; case XPathFunctionFactory.DayFromDateTime: if (args.Count == 1) { xpathFunc = new XPathDayFromDateTimeFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath day-from-dateTime() function"); } break; case XPathFunctionFactory.EncodeForURI: if (args.Count == 1) { xpathFunc = new XPathEncodeForUriFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath encode-for-uri() function"); } break; case XPathFunctionFactory.EndsWith: if (args.Count == 2) { xpathFunc = new XPathEndsWithFunction(args.First(), args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath ends-with() function"); } break; #if !NO_WEB case XPathFunctionFactory.EscapeHtmlURI: if (args.Count == 1) { xpathFunc = new XPathEscapeHtmlUriFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath escape-html-uri() function"); } break; #endif case XPathFunctionFactory.False: if (args.Count == 0) { xpathFunc = new BooleanExpressionTerm(false); } else { throw new RdfParseException("Incorrect number of arguments for the XPath false() function"); } break; case XPathFunctionFactory.Floor: if (args.Count == 1) { xpathFunc = new XPathFloorFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath floor() function"); } break; case XPathFunctionFactory.HoursFromDateTime: if (args.Count == 1) { xpathFunc = new XPathHoursFromDateTimeFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath hours-from-dateTime() function"); } break; case XPathFunctionFactory.LowerCase: if (args.Count == 1) { xpathFunc = new XPathLowerCaseFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath lower-case() function"); } break; case XPathFunctionFactory.Matches: if (args.Count == 2) { xpathFunc = new RegexFunction(args.First(), args.Last()); } else if (args.Count == 3) { xpathFunc = new RegexFunction(args.First(), args[1], args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath matches() function"); } break; case XPathFunctionFactory.MinutesFromDateTime: if (args.Count == 1) { xpathFunc = new XPathMinutesFromDateTimeFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath minutes-from-dateTime() function"); } break; case XPathFunctionFactory.MonthFromDateTime: if (args.Count == 1) { xpathFunc = new XPathMonthFromDateTimeFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath month-from-dateTime() function"); } break; case XPathFunctionFactory.NormalizeSpace: if (args.Count == 1) { xpathFunc = new XPathNormalizeSpaceFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath normalize-space() function"); } break; #if !NO_NORM case XPathFunctionFactory.NormalizeUnicode: if (args.Count == 1) { xpathFunc = new XPathNormalizeUnicodeFunction(args.First()); } else if (args.Count == 2) { xpathFunc = new XPathNormalizeUnicodeFunction(args.First(), args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath normalize-space() function"); } break; #endif case XPathFunctionFactory.Not: if (args.Count == 1) { xpathFunc = new NegationExpression(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath not() function"); } break; case XPathFunctionFactory.Replace: if (args.Count == 3) { xpathFunc = new XPathReplaceFunction(args.First(), args[1], args.Last()); } else if (args.Count == 4) { xpathFunc = new XPathReplaceFunction(args.First(), args[1], args[2], args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath replace() function"); } break; case XPathFunctionFactory.Round: if (args.Count == 1) { xpathFunc = new XPathRoundFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath round() function"); } break; #if !SILVERLIGHT case XPathFunctionFactory.RoundHalfToEven: if (args.Count == 1) { xpathFunc = new XPathRoundHalfToEvenFunction(args.First()); } else if (args.Count == 2) { xpathFunc = new XPathRoundHalfToEvenFunction(args.First(), args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath round-half-to-even() function"); } break; #endif case XPathFunctionFactory.SecondsFromDateTime: if (args.Count == 1) { xpathFunc = new XPathSecondsFromDateTimeFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath seconds-from-dateTime() function"); } break; case XPathFunctionFactory.StartsWith: if (args.Count == 2) { xpathFunc = new XPathStartsWithFunction(args.First(), args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath starts-with() function"); } break; case XPathFunctionFactory.StringJoin: if (args.Count == 1) { xpathFunc = new NonNumericAggregateExpressionTerm(new XPathStringJoinFunction(args.First())); } else if (args.Count == 2) { xpathFunc = new NonNumericAggregateExpressionTerm(new XPathStringJoinFunction(args.First(), args.Last())); } else { throw new RdfParseException("Incorrect number of arguments for the XPath string-join() function"); } break; case XPathFunctionFactory.StringLength: if (args.Count == 1) { xpathFunc = new XPathStringLengthFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath string-length() function"); } break; case XPathFunctionFactory.Substring: if (args.Count == 2) { xpathFunc = new XPathSubstringFunction(args.First(), args.Last()); } else if (args.Count == 3) { xpathFunc = new XPathSubstringFunction(args.First(), args[1], args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath substring() function"); } break; case XPathFunctionFactory.SubstringAfter: if (args.Count == 2) { xpathFunc = new XPathSubstringAfterFunction(args.First(), args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath substring-after() function"); } break; case XPathFunctionFactory.SubstringBefore: if (args.Count == 2) { xpathFunc = new XPathSubstringBeforeFunction(args.First(), args.Last()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath substring-before() function"); } break; case XPathFunctionFactory.TimezoneFromDateTime: if (args.Count == 1) { xpathFunc = new XPathTimezoneFromDateTimeFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath timezone-from-dateTime() function"); } break; case XPathFunctionFactory.Translate: throw new NotSupportedException("XPath translate() function is not supported"); case XPathFunctionFactory.True: if (args.Count == 0) { xpathFunc = new BooleanExpressionTerm(true); } else { throw new RdfParseException("Incorrect number of arguments for the XPath true() function"); } break; case XPathFunctionFactory.UpperCase: if (args.Count == 1) { xpathFunc = new XPathUpperCaseFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath upper-case() function"); } break; case XPathFunctionFactory.YearFromDateTime: if (args.Count == 1) { xpathFunc = new XPathYearFromDateTimeFunction(args.First()); } else { throw new RdfParseException("Incorrect number of arguments for the XPath year-from-dateTime() function"); } break; } if (xpathFunc != null) { expr = xpathFunc; return true; } } expr = null; return false; }
private ISparqlExpression TryParseAggregateExpression(Queue<IToken> tokens) { if (this._syntax == SparqlQuerySyntax.Sparql_1_0) throw new RdfParseException("Aggregates are not permitted in SPARQL 1.0"); IToken agg = tokens.Dequeue(); ISparqlExpression aggExpr = null; bool distinct = false, all = false; bool scalarArgs = false; //Expect a Left Bracket next IToken next = tokens.Dequeue(); if (next.TokenType != Token.LEFTBRACKET) { throw Error("Unexpected Token '" + next.GetType().ToString() + "', expected a Left Bracket after an Aggregate Keyword", next); } //Then a possible DISTINCT/ALL next = tokens.Peek(); if (next.TokenType == Token.DISTINCT) { distinct = true; tokens.Dequeue(); } next = tokens.Peek(); if (next.TokenType == Token.ALL || next.TokenType == Token.MULTIPLY) { all = true; tokens.Dequeue(); } next = tokens.Peek(); //If we've seen an ALL then we need the closing bracket if (all && next.TokenType != Token.RIGHTBRACKET) { throw Error("Unexpected Token '" + next.GetType().ToString() + "', expected a Right Bracket after the * specifier in an aggregate to terminate the aggregate", next); } else if (all && agg.TokenType != Token.COUNT) { throw new RdfQueryException("Cannot use the * specifier in aggregates other than COUNT"); } else if (!all) { //If it's not an all then we expect some expression(s) //Gather the Tokens and parse the Expression Queue<IToken> subtokens = new Queue<IToken>(); int openBrackets = 1; List<ISparqlExpression> expressions = new List<ISparqlExpression>(); while (openBrackets > 0) { subtokens = new Queue<IToken>(); next = tokens.Dequeue(); do { if (next.TokenType == Token.LEFTBRACKET) { openBrackets++; } else if (next.TokenType == Token.RIGHTBRACKET) { openBrackets--; } else if (next.TokenType == Token.COMMA) { //If we see a comma when we only have 1 bracket open then it is separating argument expressions if (openBrackets == 1) { break; } } else if (next.TokenType == Token.SEMICOLON) { //If we see a semicolon when we only have 1 bracket open then this indicates we have scalar arguments in-use if (openBrackets == 1) { scalarArgs = true; break; } } if (openBrackets > 0) { subtokens.Enqueue(next); next = tokens.Dequeue(); } } while (openBrackets > 0); //Parse this expression and add to the list of expressions we're concatenating expressions.Add(this.Parse(subtokens)); //Once we've hit the ; for the scalar arguments then we can stop looking for expressions if (scalarArgs) break; //If we've hit a , then openBrackets will still be one and we'll go around again looking for another expression //Otherwise we've reached the end of the aggregate and there was no ; for scalar arguments } if (expressions.Count == 0) throw new RdfParseException("Aggregate must have at least one argument expression unless they are a COUNT(*)"); if (agg.TokenType == Token.GROUPCONCAT) { aggExpr = new XPathConcatFunction(expressions); } else { if (expressions.Count > 1) throw new RdfParseException("The " + agg.Value + " aggregate does not support more than one argument expression"); aggExpr = expressions.First(); } } else { tokens.Dequeue(); } //If the aggregate uses scalar arguments then we'll parse them here Dictionary<String, ISparqlExpression> scalarArguments = new Dictionary<string, ISparqlExpression>(); if (scalarArgs) { scalarArguments = this.TryParseScalarArguments(agg, tokens); } //Now we need to generate the actual expression switch (agg.TokenType) { case Token.AVG: //AVG Aggregate if (aggExpr is VariableExpressionTerm) { return new AggregateExpressionTerm(new AverageAggregate((VariableExpressionTerm)aggExpr, distinct)); } else { return new AggregateExpressionTerm(new AverageAggregate(aggExpr, distinct)); } case Token.COUNT: //COUNT Aggregate if (all) { if (distinct) { return new AggregateExpressionTerm(new CountAllDistinctAggregate()); } else { return new AggregateExpressionTerm(new CountAllAggregate()); } } else if (aggExpr is VariableExpressionTerm) { if (distinct) { return new AggregateExpressionTerm(new CountDistinctAggregate((VariableExpressionTerm)aggExpr)); } else { return new AggregateExpressionTerm(new CountAggregate((VariableExpressionTerm)aggExpr)); } } else { if (distinct) { return new AggregateExpressionTerm(new CountDistinctAggregate(aggExpr)); } else { return new AggregateExpressionTerm(new CountAggregate(aggExpr)); } } case Token.GROUPCONCAT: if (scalarArgs) { if (!scalarArguments.ContainsKey(SparqlSpecsHelper.SparqlKeywordSeparator)) throw new RdfParseException("The GROUP_CONCAT aggregate has Scalar Arguments but does not have the expected SEPARATOR argument"); return new NonNumericAggregateExpressionTerm(new GroupConcatAggregate(aggExpr, scalarArguments[SparqlSpecsHelper.SparqlKeywordSeparator], distinct)); } else { return new NonNumericAggregateExpressionTerm(new GroupConcatAggregate(aggExpr, distinct)); } case Token.MAX: //MAX Aggregate if (aggExpr is VariableExpressionTerm) { return new NonNumericAggregateExpressionTerm(new MaxAggregate((VariableExpressionTerm)aggExpr, distinct)); } else { return new NonNumericAggregateExpressionTerm(new MaxAggregate(aggExpr, distinct)); } case Token.MEDIAN: //MEDIAN Aggregate if (this._syntax != SparqlQuerySyntax.Extended) throw new RdfParseException("The MEDIAN aggregate is only supported when the Syntax is set to Extended."); if (aggExpr is VariableExpressionTerm) { return new NonNumericAggregateExpressionTerm(new MedianAggregate((VariableExpressionTerm)aggExpr, distinct)); } else { return new NonNumericAggregateExpressionTerm(new MedianAggregate(aggExpr, distinct)); } case Token.MIN: //MIN Aggregate if (aggExpr is VariableExpressionTerm) { return new NonNumericAggregateExpressionTerm(new MinAggregate((VariableExpressionTerm)aggExpr, distinct)); } else { return new NonNumericAggregateExpressionTerm(new MinAggregate(aggExpr, distinct)); } case Token.MODE: //MODE Aggregate if (this._syntax != SparqlQuerySyntax.Extended) throw new RdfParseException("The MODE aggregate is only supported when the Syntax is set to Extended."); if (aggExpr is VariableExpressionTerm) { return new NonNumericAggregateExpressionTerm(new ModeAggregate((VariableExpressionTerm)aggExpr, distinct)); } else { return new NonNumericAggregateExpressionTerm(new ModeAggregate(aggExpr, distinct)); } case Token.NMAX: //NMAX Aggregate if (this._syntax != SparqlQuerySyntax.Extended) throw new RdfParseException("The NMAX (Numeric Maximum) aggregate is only supported when the Syntax is set to Extended. To achieve an equivalent result in SPARQL 1.0/1.1 apply a FILTER to your query so the aggregated variable is only literals of the desired numeric type"); if (aggExpr is VariableExpressionTerm) { return new AggregateExpressionTerm(new NumericMaxAggregate((VariableExpressionTerm)aggExpr, distinct)); } else { return new AggregateExpressionTerm(new NumericMaxAggregate(aggExpr, distinct)); } case Token.NMIN: //NMIN Aggregate if (this._syntax != SparqlQuerySyntax.Extended) throw new RdfParseException("The NMIN (Numeric Minimum) aggregate is only supported when the Syntax is set to Extended. To achieve an equivalent result in SPARQL 1.0/1.1 apply a FILTER to your query so the aggregated variable is only literals of the desired numeric type"); if (aggExpr is VariableExpressionTerm) { return new AggregateExpressionTerm(new NumericMinAggregate((VariableExpressionTerm)aggExpr, distinct)); } else { return new AggregateExpressionTerm(new NumericMinAggregate(aggExpr, distinct)); } case Token.SAMPLE: //SAMPLE Aggregate if (distinct) throw new RdfParseException("DISTINCT modifier is not valid for the SAMPLE aggregate"); return new NonNumericAggregateExpressionTerm(new SampleAggregate(aggExpr)); case Token.SUM: //SUM Aggregate if (aggExpr is VariableExpressionTerm) { return new AggregateExpressionTerm(new SumAggregate((VariableExpressionTerm)aggExpr, distinct)); } else { return new AggregateExpressionTerm(new SumAggregate(aggExpr, distinct)); } default: //Should have already handled this but have to have it to keep the compiler happy throw Error("Cannot parse an Aggregate since '" + agg.GetType().ToString() + "' is not an Aggregate Keyword Token", agg); } }