/// <summary> /// Thread Worker method which writes Graphs to the output /// </summary> /// <param name="globalContext">Context for writing the Store</param> private void SaveGraphs(TriGWriterContext globalContext) { try { Uri u = globalContext.GetNextUri(); while (u != null) { //Get the Graph from the Store if (WriterHelper.IsDefaultGraph(u) && !globalContext.Store.HasGraph(u)) u = null; IGraph g = globalContext.Store.Graphs[u]; //Generate the Graph Output and add to Stream TurtleWriterContext context = new TurtleWriterContext(g, new System.IO.StringWriter(), globalContext.PrettyPrint, globalContext.HighSpeedModePermitted); if (globalContext.CompressionLevel > WriterCompressionLevel.None) { context.NodeFormatter = new TurtleFormatter(globalContext.QNameMapper); } else { context.NodeFormatter = new UncompressedTurtleFormatter(); } String graphContent = this.GenerateGraphOutput(globalContext, context); try { Monitor.Enter(globalContext.Output); globalContext.Output.WriteLine(graphContent); globalContext.Output.Flush(); } catch { throw; } finally { Monitor.Exit(globalContext.Output); } //Get the Next Uri u = globalContext.GetNextUri(); } } catch (ThreadAbortException) { //We've been terminated, don't do anything #if !SILVERLIGHT Thread.ResetAbort(); #endif } catch (Exception ex) { throw new RdfStorageException("Error in Threaded Writer in Thread ID " + Thread.CurrentThread.ManagedThreadId, ex); } }
/// <summary> /// Generates Output for Nodes in Turtle syntax /// </summary> /// <param name="globalContext">Context for writing the Store</param> /// <param name="context">Context for writing the Graph</param> /// <param name="n">Node to generate output for</param> /// <param name="segment">Segment of the Triple being written</param> /// <returns></returns> private String GenerateNodeOutput(TriGWriterContext globalContext, TurtleWriterContext context, INode n, TripleSegment segment) { switch (n.NodeType) { case NodeType.Blank: if (segment == TripleSegment.Predicate) throw new RdfOutputException(WriterErrorMessages.BlankPredicatesUnserializable("TriG")); break; case NodeType.GraphLiteral: throw new RdfOutputException(WriterErrorMessages.GraphLiteralsUnserializable("TriG")); case NodeType.Literal: if (segment == TripleSegment.Subject) throw new RdfOutputException(WriterErrorMessages.LiteralSubjectsUnserializable("TriG")); if (segment == TripleSegment.Predicate) throw new RdfOutputException(WriterErrorMessages.LiteralPredicatesUnserializable("TriG")); break; case NodeType.Uri: break; default: throw new RdfOutputException(WriterErrorMessages.UnknownNodeTypeUnserializable("TriG")); } return context.NodeFormatter.Format(n, segment); }
/// <summary> /// Generates the Output for a Graph as a String in TriG syntax /// </summary> /// <param name="globalContext">Context for writing the Store</param> /// <param name="context">Context for writing the Graph</param> /// <returns></returns> private String GenerateGraphOutput(TriGWriterContext globalContext, TurtleWriterContext context) { if (!WriterHelper.IsDefaultGraph(context.Graph.BaseUri)) { //Named Graph String gname; String sep = (globalContext.N3CompatabilityMode) ? " = " : " "; if (globalContext.CompressionLevel > WriterCompressionLevel.None && globalContext.QNameMapper.ReduceToQName(context.Graph.BaseUri.ToString(), out gname)) { if (TurtleSpecsHelper.IsValidQName(gname)) { context.Output.WriteLine(gname + sep + "{"); } else { context.Output.WriteLine("<" + context.UriFormatter.FormatUri(context.Graph.BaseUri) + ">" + sep + "{"); } } else { context.Output.WriteLine("<" + context.UriFormatter.FormatUri(context.Graph.BaseUri) + ">" + sep + "{"); } } else { context.Output.WriteLine("{"); } //Generate Triples this.GenerateTripleOutput(globalContext, context); //Close the Graph context.Output.WriteLine("}"); return context.Output.ToString(); }
/// <summary> /// Generates the Output for a Triple as a String in Turtle syntax /// </summary> /// <param name="globalContext">Context for writing the Store</param> /// <param name="context">Context for writing the Graph</param> private void GenerateTripleOutput(TriGWriterContext globalContext, TurtleWriterContext context) { //Decide which write mode to use bool hiSpeed = false; double subjNodes = context.Graph.Triples.SubjectNodes.Count(); double triples = context.Graph.Triples.Count; if ((subjNodes / triples) > 0.75) hiSpeed = true; if (globalContext.CompressionLevel == WriterCompressionLevel.None || hiSpeed && context.HighSpeedModePermitted) { //Use High Speed Write Mode String indentation = new String(' ', 4); context.Output.Write(indentation); if (globalContext.CompressionLevel > WriterCompressionLevel.None) context.Output.WriteLine("# Written using High Speed Mode"); foreach (Triple t in context.Graph.Triples) { context.Output.Write(indentation); context.Output.Write(this.GenerateNodeOutput(globalContext, context, t.Subject, TripleSegment.Subject)); context.Output.Write(' '); context.Output.Write(this.GenerateNodeOutput(globalContext, context, t.Predicate, TripleSegment.Predicate)); context.Output.Write(' '); context.Output.Write(this.GenerateNodeOutput(globalContext, context, t.Object, TripleSegment.Object)); context.Output.WriteLine("."); } } else { //Get the Triples as a Sorted List List<Triple> ts = context.Graph.Triples.ToList(); ts.Sort(); //Variables we need to track our writing INode lastSubj, lastPred; lastSubj = lastPred = null; int subjIndent = 0, predIndent = 0; int baseIndent = 4; String temp; for (int i = 0; i < ts.Count; i++) { Triple t = ts[i]; if (lastSubj == null || !t.Subject.Equals(lastSubj)) { //Terminate previous Triples if (lastSubj != null) context.Output.WriteLine("."); if (context.PrettyPrint) context.Output.Write(new String(' ', baseIndent)); //Start a new set of Triples temp = this.GenerateNodeOutput(globalContext, context, t.Subject, TripleSegment.Subject); context.Output.Write(temp); context.Output.Write(" "); subjIndent = baseIndent + temp.Length + 1; lastSubj = t.Subject; //Write the first Predicate temp = this.GenerateNodeOutput(globalContext, context, t.Predicate, TripleSegment.Predicate); context.Output.Write(temp); context.Output.Write(" "); predIndent = temp.Length + 1; lastPred = t.Predicate; } else if (lastPred == null || !t.Predicate.Equals(lastPred)) { //Terminate previous Predicate Object list context.Output.WriteLine(";"); if (context.PrettyPrint) context.Output.Write(new String(' ', subjIndent)); //Write the next Predicate temp = this.GenerateNodeOutput(globalContext, context, t.Predicate, TripleSegment.Predicate); context.Output.Write(temp); context.Output.Write(" "); predIndent = temp.Length + 1; lastPred = t.Predicate; } else { //Continue Object List context.Output.WriteLine(","); if (context.PrettyPrint) context.Output.Write(new String(' ', subjIndent + predIndent)); } //Write the Object context.Output.Write(this.GenerateNodeOutput(globalContext, context, t.Object, TripleSegment.Object)); } //Terminate Triples if (ts.Count > 0) context.Output.WriteLine("."); } }
/// <summary> /// Saves a Store in TriG (Turtle with Named Graphs) format /// </summary> /// <param name="store">Store to save</param> /// <param name="parameters">Parameters indicating a Stream to write to</param> public void Save(ITripleStore store, IStoreParams parameters) { //Try and determine the TextWriter to output to TriGWriterContext context = null; if (parameters is StreamParams) { //Create a new Writer Context ((StreamParams)parameters).Encoding = new UTF8Encoding(Options.UseBomForUtf8); context = new TriGWriterContext(store, ((StreamParams)parameters).StreamWriter, this._prettyprint, this._allowHiSpeed, this._compressionLevel, this._n3compat); } else if (parameters is TextWriterParams) { context = new TriGWriterContext(store, ((TextWriterParams)parameters).TextWriter, this._prettyprint, this._allowHiSpeed, this._compressionLevel, this._n3compat); } if (context != null) { //Check there's something to do if (context.Store.Graphs.Count == 0) { context.Output.Close(); return; } //Write the Header of the File foreach (IGraph g in context.Store.Graphs) { context.NamespaceMap.Import(g.NamespaceMap); } if (context.CompressionLevel > WriterCompressionLevel.None) { //Only add @prefix declarations if compression is enabled context.QNameMapper = new ThreadSafeQNameOutputMapper(context.NamespaceMap); foreach (String prefix in context.NamespaceMap.Prefixes) { if (TurtleSpecsHelper.IsValidQName(prefix + ":")) { context.Output.WriteLine("@prefix " + prefix + ": <" + context.FormatUri(context.NamespaceMap.GetNamespaceUri(prefix)) + ">."); } } context.Output.WriteLine(); } else { context.QNameMapper = new ThreadSafeQNameOutputMapper(new NamespaceMapper(true)); } if (this._useMultiThreading) { //Standard Multi-Threaded Writing //Queue the Graphs to be written foreach (IGraph g in context.Store.Graphs) { if (g.BaseUri == null) { context.Add(new Uri(GraphCollection.DefaultGraphUri)); } else { context.Add(g.BaseUri); } } //Start making the async calls List<IAsyncResult> results = new List<IAsyncResult>(); SaveGraphsDelegate d = new SaveGraphsDelegate(this.SaveGraphs); for (int i = 0; i < this._threads; i++) { results.Add(d.BeginInvoke(context, null, null)); } //Wait for all the async calls to complete WaitHandle.WaitAll(results.Select(r => r.AsyncWaitHandle).ToArray()); RdfThreadedOutputException outputEx = new RdfThreadedOutputException(WriterErrorMessages.ThreadedOutputFailure("TriG")); foreach (IAsyncResult result in results) { try { d.EndInvoke(result); } catch (Exception ex) { outputEx.AddException(ex); } } //Make sure to close the output context.Output.Close(); //If there were any errors we'll throw an RdfThreadedOutputException now if (outputEx.InnerExceptions.Any()) throw outputEx; } else { try { //Optional Single Threaded Writing foreach (IGraph g in store.Graphs) { TurtleWriterContext graphContext = new TurtleWriterContext(g, new System.IO.StringWriter(), context.PrettyPrint, context.HighSpeedModePermitted); if (context.CompressionLevel > WriterCompressionLevel.None) { graphContext.NodeFormatter = new TurtleFormatter(context.QNameMapper); } else { graphContext.NodeFormatter = new UncompressedTurtleFormatter(); } context.Output.WriteLine(this.GenerateGraphOutput(context, graphContext)); } //Make sure to close the output context.Output.Close(); } catch { try { //Close the output context.Output.Close(); } catch { //No catch actions, just cleaning up the output stream } throw; } } } else { throw new RdfStorageException("Parameters for the TriGWriter must be of the type StreamParams/TextWriterParams"); } }