/// <summary>
        /// Generates a SPARQL Service Description Graph for the given Update Handler Configuration or uses the configuration supplied Description Graph
        /// </summary>
        /// <param name="context">HTTP Context</param>
        /// <param name="config">Update Handler Configuration</param>
        /// <param name="descripUri">Base URI of the Description</param>
        /// <returns></returns>
        public static IGraph GetServiceDescription(HttpContext context, BaseUpdateHandlerConfiguration config, Uri descripUri)
        {
            //Use user specified Service Description if present
            if (config.ServiceDescription != null) return config.ServiceDescription;

            IGraph g = SparqlServiceDescriber.GetNewGraph();

            //Add the Top Level Node representing the Service
            IUriNode descrip = g.CreateUriNode(descripUri);
            IUriNode rdfType = g.CreateUriNode(new Uri(RdfSpecsHelper.RdfType));
            IUriNode service = g.CreateUriNode("sd:" + ClassService);
            g.Assert(descrip, rdfType, service);

            //Add its sd:url
            IUriNode url = g.CreateUriNode("sd:" + PropertyUrl);
            g.Assert(descrip, url, descrip);

            //Add the sd:supportedLanguage
            IUriNode supportedLang = g.CreateUriNode("sd:" + PropertySupportedLanguage);
            g.Assert(descrip, supportedLang, g.CreateUriNode("sd:" + InstanceSparql11Update));

            //Add Features and Dataset Description
            //First add descriptions for Global Expression Factories
            IUriNode extensionFunction = g.CreateUriNode("sd:" + PropertyExtensionFunction);
            IUriNode extensionAggregate = g.CreateUriNode("sd:" + PropertyExtensionAggregate);
            foreach (ISparqlCustomExpressionFactory factory in SparqlExpressionFactory.Factories)
            {
                foreach (Uri u in factory.AvailableExtensionFunctions)
                {
                    g.Assert(descrip, extensionFunction, g.CreateUriNode(u));
                }
                foreach (Uri u in factory.AvailableExtensionAggregates)
                {
                    g.Assert(descrip, extensionAggregate, g.CreateUriNode(u));
                }
            }

            //Then get the Configuration Object to add any other Feature Descriptions it wishes to
            config.AddFeatureDescription(g, descrip);

            return g;
        }
        /// <summary>
        /// Processes SPARQL Update requests
        /// </summary>
        /// <param name="context">HTTP Context</param>
        public void ProcessRequest(HttpContext context)
        {
            this._config = this.LoadConfig(context);

            //Add our Standard Headers
            HandlerHelper.AddStandardHeaders(context, this._config);

            if (context.Request.HttpMethod.Equals("OPTIONS"))
            {
                //OPTIONS requests always result in the Service Description document
                IGraph svcDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, new Uri(context.Request.Url.AbsoluteUri));
                HandlerHelper.SendToClient(context, svcDescrip, this._config);
                return;
            }

            //See if there has been an update submitted
            String updateText = context.Request.QueryString["update"];
            if (updateText == null || updateText.Equals(String.Empty))
            {
                updateText = context.Request.Form["update"];
            }

            //If no Update sent either show Update Form or give a HTTP 400 response
            if (updateText == null || updateText.Equals(String.Empty))
            {
                //If there is no Update we may return the SPARQL Service Description where appropriate
                try
                {
                    //If we might show the Update Form only show the Description if the selected writer is
                    //not a HTML writer
                    MimeTypeDefinition definition = MimeTypesHelper.GetDefinitions(HandlerHelper.GetAcceptTypes(context)).FirstOrDefault(d => d.CanWriteRdf);
                    if (definition != null)
                    {
                        IRdfWriter writer = definition.GetRdfWriter();
                        if (!this._config.ShowUpdateForm || !(writer is IHtmlWriter))
                        {
                            //If not a HTML Writer selected OR not showing Update Form then show the Service Description Graph
                            //unless an error occurs creating it
                            IGraph serviceDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, new Uri(context.Request.Url.AbsoluteUri));
                            context.Response.ContentType = definition.CanonicalMimeType;
                            context.Response.ContentEncoding = definition.Encoding;
                            writer.Save(serviceDescrip, new StreamWriter(context.Response.OutputStream, definition.Encoding));
                            return;
                        }
                    }
                }
                catch
                {
                    //Ignore Exceptions - we'll just show the Query Form or return a 400 Bad Request instead
                }

                //If a Writer can't be selected then we'll either show the Update Form or return a 400 Bad Request
                if (this._config.ShowUpdateForm)
                {
                    this.ShowUpdateForm(context);
                }
                else
                {
                    context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                }
                return;
            }

            try
            {
                //Now we're going to parse the Updates
                SparqlUpdateParser parser = new SparqlUpdateParser();
                parser.ExpressionFactories = this._config.ExpressionFactories;
                SparqlUpdateCommandSet commands = parser.ParseFromString(updateText);

                //Check whether we need to use authentication
                //If there are no user groups then no authentication is in use so we default to authenticated with no per-action authentication needed
                bool isAuth = true, requireActionAuth = false;
                if (this._config.UserGroups.Any())
                {
                    //If we have user
                    isAuth = HandlerHelper.IsAuthenticated(context, this._config.UserGroups);
                    requireActionAuth = true;
                }
                if (!isAuth) return;

                //First check actions to see whether they are all permissible
                foreach (SparqlUpdateCommand cmd in commands.Commands)
                {
                    //Authenticate each action
                    bool actionAuth = true;
                    if (requireActionAuth) actionAuth = HandlerHelper.IsAuthenticated(context, this._config.UserGroups, this.GetPermissionAction(cmd));
                    if (!actionAuth)
                    {
                        throw new SparqlUpdateException("You are not authorised to perform the " + this.GetPermissionAction(cmd) + " action");
                    }
                }

                //Then assuming we got here this means all our actions are permitted so now we can process the updates
                this.ProcessUpdates(commands);

                //Flush outstanding changes
                this._config.Processor.Flush();

                //Update the Cache as the request may have changed the endpoint
                this.UpdateConfig(context);
            }
            catch (RdfParseException parseEx)
            {
                HandleErrors(context, "Parsing Error", updateText, parseEx);
            }
            catch (SparqlUpdateException updateEx)
            {
                HandleErrors(context, "Update Error", updateText, updateEx);
            }
            catch (RdfException rdfEx)
            {
                HandleErrors(context, "RDF Error", updateText, rdfEx);
            }
            catch (Exception ex)
            {
                HandleErrors(context, "Error", updateText, ex);
            }
        }
        /// <summary>
        /// Processes SPARQL Update requests
        /// </summary>
        /// <param name="context">HTTP Context</param>
        public void ProcessRequest(HttpContext context)
        {
            this._config = this.LoadConfig(context);

            //Add our Standard Headers
            HandlerHelper.AddStandardHeaders(context, this._config);

            if (context.Request.HttpMethod.Equals("OPTIONS"))
            {
                //OPTIONS requests always result in the Service Description document
                IGraph svcDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, UriFactory.Create(context.Request.Url.AbsoluteUri));
                HandlerHelper.SendToClient(context, svcDescrip, this._config);
                return;
            }

            //See if there has been an update submitted
            String updateText = null;
            if (context.Request.ContentType != null)
            {
                if (context.Request.ContentType.Equals(MimeTypesHelper.WWWFormURLEncoded))
                {
                    updateText = context.Request.Form["update"];
                }
                else if (context.Request.ContentType.Equals(MimeTypesHelper.SparqlUpdate))
                {
                    updateText = new StreamReader(context.Request.InputStream).ReadToEnd();
                }
            }
            else
            {
                updateText = context.Request.Form["update"];
            }

            //If no Update sent either show Update Form or give a HTTP 400 response
            if (updateText == null || updateText.Equals(String.Empty))
            {
                //If there is no Update we may return the SPARQL Service Description where appropriate
                try
                {
                    //If we might show the Update Form only show the Description if the selected writer is
                    //not a HTML writer
                    MimeTypeDefinition definition = MimeTypesHelper.GetDefinitions(HandlerHelper.GetAcceptTypes(context)).FirstOrDefault(d => d.CanWriteRdf);
                    if (definition != null)
                    {
                        IRdfWriter writer = definition.GetRdfWriter();
                        if (!this._config.ShowUpdateForm || !(writer is IHtmlWriter))
                        {
                            //If not a HTML Writer selected OR not showing Update Form then show the Service Description Graph
                            //unless an error occurs creating it
                            IGraph serviceDescrip = SparqlServiceDescriber.GetServiceDescription(context, this._config, UriFactory.Create(context.Request.Url.AbsoluteUri));
                            context.Response.ContentType = definition.CanonicalMimeType;
                            context.Response.ContentEncoding = definition.Encoding;
                            writer.Save(serviceDescrip, new StreamWriter(context.Response.OutputStream, definition.Encoding));
                            return;
                        }
                    }
                }
                catch
                {
                    //Ignore Exceptions - we'll just show the Query Form or return a 400 Bad Request instead
                }

                //If a Writer can't be selected then we'll either show the Update Form or return a 400 Bad Request
                if (this._config.ShowUpdateForm)
                {
                    this.ShowUpdateForm(context);
                }
                else
                {
                    context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
                }
                return;
            }

            //Get Other options associated with this update
            List<String> userDefaultGraphs = new List<String>();
            List<String> userNamedGraphs = new List<String>();

            //Get the USING URIs (if any)
            if (context.Request.QueryString["using-graph-uri"] != null)
            {
                userDefaultGraphs.AddRange(context.Request.QueryString.GetValues("using-graph-uri"));
            }
            else if (context.Request.Form["using-graph-uri"] != null)
            {
                userDefaultGraphs.AddRange(context.Request.Form.GetValues("using-graph-uri"));
            }
            //Get the USING NAMED URIs (if any)
            if (context.Request.QueryString["using-named-graph-uri"] != null)
            {
                userNamedGraphs.AddRange(context.Request.QueryString.GetValues("using-named-graph-uri"));
            }
            else if (context.Request.Form["using-named-graph-uri"] != null)
            {
                userNamedGraphs.AddRange(context.Request.Form.GetValues("using-named-graph-uri"));
            }

            try
            {
                //Now we're going to parse the Updates
                SparqlUpdateParser parser = new SparqlUpdateParser();
                parser.ExpressionFactories = this._config.ExpressionFactories;
                SparqlUpdateCommandSet commands = parser.ParseFromString(updateText);

                //Check whether we need to use authentication
                //If there are no user groups then no authentication is in use so we default to authenticated with no per-action authentication needed
                bool isAuth = true, requireActionAuth = false;
                if (this._config.UserGroups.Any())
                {
                    //If we have user
                    isAuth = HandlerHelper.IsAuthenticated(context, this._config.UserGroups);
                    requireActionAuth = true;
                }
                if (!isAuth) return;

                //First check actions to see whether they are all permissible and apply USING/USING NAMED parameters
                foreach (SparqlUpdateCommand cmd in commands.Commands)
                {
                    //Authenticate each action
                    bool actionAuth = true;
                    if (requireActionAuth) actionAuth = HandlerHelper.IsAuthenticated(context, this._config.UserGroups, this.GetPermissionAction(cmd));
                    if (!actionAuth)
                    {
                        throw new SparqlUpdatePermissionException("You are not authorised to perform the " + this.GetPermissionAction(cmd) + " action");
                    }

                    //Check whether we need to (and are permitted to) apply USING/USING NAMED parameters
                    if (userDefaultGraphs.Count > 0 || userNamedGraphs.Count > 0)
                    {
                        BaseModificationCommand modify = cmd as BaseModificationCommand;
                        if (modify != null)
                        {
                            if (modify.GraphUri != null || modify.UsingUris.Any() || modify.UsingNamedUris.Any())
                            {
                                //Invalid if a command already has a WITH/USING/USING NAMED
                                throw new SparqlUpdateMalformedException("A command in your update request contains a WITH/USING/USING NAMED clause but you have also specified one/both of the using-graph-uri or using-named-graph-uri parameters which is not permitted by the SPARQL Protocol");
                            }
                            else
                            {
                                //Otherwise go ahead and apply
                                userDefaultGraphs.ForEach(u => modify.AddUsingUri(UriFactory.Create(u)));
                                userNamedGraphs.ForEach(u => modify.AddUsingNamedUri(UriFactory.Create(u)));
                            }
                        }
                    }
                }

                //Then assuming we got here this means all our actions are permitted so now we can process the updates
                this.ProcessUpdates(commands);

                //Flush outstanding changes
                this._config.Processor.Flush();

                //Update the Cache as the request may have changed the endpoint
                this.UpdateConfig(context);
            }
            catch (RdfParseException parseEx)
            {
                HandleErrors(context, "Parsing Error", updateText, parseEx, (int)HttpStatusCode.BadRequest);
            }
            catch (SparqlUpdatePermissionException permEx)
            {
                HandleErrors(context, "Permissions Error", updateText, permEx, (int)HttpStatusCode.Forbidden);
            }
            catch (SparqlUpdateMalformedException malEx)
            {
                HandleErrors(context, "Malformed Update Error", updateText, malEx, (int)HttpStatusCode.BadRequest);
            }
            catch (SparqlUpdateException updateEx)
            {
                HandleErrors(context, "Update Error", updateText, updateEx);
            }
            catch (RdfException rdfEx)
            {
                HandleErrors(context, "RDF Error", updateText, rdfEx);
            }
            catch (Exception ex)
            {
                HandleErrors(context, "Error", updateText, ex);
            }
        }