/// <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);
            }
        }