private void GenerateCollectionOutput(RdfXmlWriterContext context, INode key)
        {
            OutputRdfCollection c = context.Collections[key];

            c.HasBeenWritten = true;

            if (c.IsExplicit)
            {
                if (c.Triples.Count == 0)
                {
                    //If No Triples then an isolated blank node so add rdf:nodeID and return
                    context.Writer.WriteAttributeString("rdf", "nodeID", NamespaceMapper.RDF, context.BlankNodeMapper.GetOutputID(((IBlankNode)key).InternalID));
                    return;
                }

                //First see if there is a typed triple available (only applicable if we have more than one triple)
                INode  rdfType    = context.Graph.CreateUriNode(UriFactory.Create(RdfSpecsHelper.RdfType));
                Triple typeTriple = c.Triples.FirstOrDefault(t => t.Predicate.Equals(rdfType) && t.Object.NodeType == NodeType.Uri);
                if (typeTriple != null)
                {
                    //Should be safe to invoke GenerateSubjectOutput but we can't allow rdf:Description
                    this.GenerateSubjectOutput(context, c.Triples, false);
                }
                else
                {
                    //Otherwise we invoke GeneratePredicateOutput (and use rdf:parseType="Resource" if there was more than 1 triple)
                    context.Writer.WriteAttributeString("rdf", "parseType", NamespaceMapper.RDF, "Resource");
                    foreach (Triple t in c.Triples)
                    {
                        this.GeneratePredicateOutput(context, t);
                        context.TriplesDone.Add(t);
                    }
                }
            }
            else
            {
                //If No Triples then use rdf:about rdf:nil and return
                if (c.Triples.Count == 0)
                {
                    context.Writer.WriteAttributeString("rdf", "about", NamespaceMapper.RDF, RdfSpecsHelper.RdfListNil);
                    return;
                }

                //Going to need rdf:parseType="Resource" on current predicate
                context.Writer.WriteAttributeString("rdf", "parseType", NamespaceMapper.RDF, "Resource");

                this.GenerateCollectionItemOutput(context, c);
            }
        }
Exemple #2
0
        /// <summary>
        /// Helper method which finds Collections expressed in the Graph which can be compressed into concise collection syntax constructs in some RDF syntaxes
        /// </summary>
        /// <param name="context">Writer Context</param>
        /// <param name="mode">Collection Search Mode</param>
        public static void FindCollections(ICollectionCompressingWriterContext context, CollectionSearchMode mode)
        {
            //Prepare the RDF Nodes we need
            INode first, rest, nil;

            first = context.Graph.CreateUriNode(UriFactory.Create(RdfSpecsHelper.RdfListFirst));
            rest  = context.Graph.CreateUriNode(UriFactory.Create(RdfSpecsHelper.RdfListRest));
            nil   = context.Graph.CreateUriNode(UriFactory.Create(RdfSpecsHelper.RdfListNil));

            //First we're going to look for implicit collections we can represent using the
            //brackets syntax of (a b c)

            if (mode == CollectionSearchMode.All || mode == CollectionSearchMode.ImplicitOnly)
            {
                //Find all rdf:rest rdf:nil Triples
                foreach (Triple t in context.Graph.GetTriplesWithPredicateObject(rest, nil))
                {
                    //If has named list node cannot compress
                    if (t.Subject.NodeType != NodeType.Blank)
                    {
                        break;
                    }

                    //Build the collection recursively
                    OutputRdfCollection c = new OutputRdfCollection(false);

                    //Get the thing that is the rdf:first related to this rdf:rest
                    IEnumerable <Triple> firsts = context.Graph.GetTriplesWithSubjectPredicate(t.Subject, first);//context.Graph.GetTriples(relfirstsel).Distinct();
                    Triple temp;
                    if (firsts.Count() > 1)
                    {
                        //Strange error
                        throw new RdfOutputException(WriterErrorMessages.MalformedCollectionWithMultipleFirsts);
                    }
                    else
                    {
                        //Stick this item onto the Stack
                        temp = firsts.First();
                        c.Triples.Add(temp);
                    }

                    //See if this thing is the rdf:rest of anything else
                    do
                    {
                        IEnumerable <Triple> ts = context.Graph.GetTriplesWithPredicateObject(rest, firsts.First().Subject);

                        //Stop when there isn't a rdf:rest
                        if (ts.Count() == 0)
                        {
                            break;
                        }

                        foreach (Triple t2 in ts)
                        {
                            firsts = context.Graph.GetTriplesWithSubjectPredicate(t2.Subject, first).Distinct();

                            if (firsts.Count() > 1)
                            {
                                //Strange error
                                throw new RdfOutputException(WriterErrorMessages.MalformedCollectionWithMultipleFirsts);
                            }
                            else
                            {
                                //Stick this item onto the Stack
                                temp = firsts.First();
                                //If Item is a named list node cannot compress
                                if (temp.Subject.NodeType != NodeType.Blank)
                                {
                                    break;
                                }
                                c.Triples.Add(temp);
                            }
                        }
                    } while (true);

                    //Can only compress if every List Node has a Blank Node Subject
                    if (c.Triples.All(x => x.Subject.NodeType == NodeType.Blank))
                    {
                        context.Collections.Add(firsts.First().Subject, c);
                    }
                }
            }

            //Now we want to look for explicit collections which are representable
            //using Blank Node syntax [p1 o1; p2 o2; p3 o3]
            if (mode == CollectionSearchMode.All || mode == CollectionSearchMode.ExplicitOnly)
            {
                foreach (IBlankNode b in context.Graph.Nodes.BlankNodes)
                {
                    if (context.Collections.ContainsKey(b))
                    {
                        continue;
                    }

                    List <Triple> ts = context.Graph.GetTriples(b).ToList();
                    ts.RemoveAll(t => t.Predicate.Equals(first));
                    ts.RemoveAll(t => t.Predicate.Equals(rest));

                    if (ts.Count <= 1)
                    {
                        //This Blank Node is only used once
                        //Add an empty explicit collection - we'll interpret this as [] later
                        context.Collections.Add(b, new OutputRdfCollection(true));
                    }
                    else
                    {
                        if (context.Graph.GetTriplesWithObject(b).Count() == 1)
                        {
                            ts.RemoveAll(t => t.Object.Equals(b));
                        }
                        OutputRdfCollection c = new OutputRdfCollection(true);
                        c.Triples.AddRange(ts);
                        context.Collections.Add(b, c);
                    }
                }
            }

            //If no collections found then no further processing
            if (context.Collections.Count == 0)
            {
                return;
            }

            //Once we've found all the Collections we need to check which are actually eligible for compression
            List <KeyValuePair <INode, OutputRdfCollection> > cs = context.Collections.ToList();

            //1 - If all the Triples pertaining to a particular Node are in the Collection then a collection is not eligible
            foreach (KeyValuePair <INode, OutputRdfCollection> kvp in cs)
            {
                OutputRdfCollection c = kvp.Value;
                if (c.IsExplicit)
                {
                    //For explicit collections if all Triples mentioning the Target Blank Node are in the Collection then can't compress
                    //If there are no Triples in the Collection then this is a single use Blank Node so can always compress
                    if (c.Triples.Count > 0 && c.Triples.Count == context.Graph.GetTriples(kvp.Key).Count())
                    {
                        context.Collections.Remove(kvp.Key);
                    }
                }
                else
                {
                    //For implicit collections if the number of Triples in the Collection is exactly ((t*3) - 1) those in the Graph then
                    //can't compress i.e. the collection is not linked to anything else
                    //Or if the number of mentions compared to the expected mentions differs by more than 1 then
                    //can't compress i.e. the collection is linked to more than one thing
                    int mentions         = context.Graph.GetTriples(kvp.Key).Count();
                    int expectedMentions = ((c.Triples.Count * 3) - 1);
                    if (expectedMentions == mentions || mentions - expectedMentions != 1)
                    {
                        context.Collections.Remove(kvp.Key);
                    }
                }
            }
            if (context.Collections.Count == 0)
            {
                return;
            }

            //2 - Look for cyclic collection dependencies
            cs = context.Collections.OrderByDescending(kvp => kvp.Value.Triples.Count).ToList();

            //First build up a dependencies table
            Dictionary <INode, List <INode> > dependencies = new Dictionary <INode, List <INode> >();

            foreach (KeyValuePair <INode, OutputRdfCollection> kvp in cs)
            {
                OutputRdfCollection c = kvp.Value;

                //Empty Blank Node Collections cannot be cyclic i.e. []
                if (c.Triples.Count == 0)
                {
                    continue;
                }

                //Otherwise check each Object of the Triples for other Blank Nodes
                List <INode> ds = new List <INode>();
                foreach (Triple t in c.Triples)
                {
                    //Only care about Blank Nodes which aren't the collection root but are the root for another collection
                    if (t.Object.NodeType == NodeType.Blank && !t.Object.Equals(kvp.Key) && context.Collections.ContainsKey(t.Object))
                    {
                        ds.Add(t.Object);
                    }
                }
                if (ds.Count > 0)
                {
                    dependencies.Add(kvp.Key, ds);
                }
            }

            //Now go back through that table looking for cycles
            foreach (INode n in dependencies.Keys)
            {
                List <INode> ds = dependencies[n];
                if (ds.Count == 0)
                {
                    continue;
                }

                foreach (INode d in ds.ToList())
                {
                    if (dependencies.ContainsKey(d))
                    {
                        ds.AddRange(dependencies[d]);
                    }
                }

                //We can tell if there is a cycle since ds will now contain n
                if (ds.Contains(n))
                {
                    context.Collections.Remove(n);
                }
            }
            if (context.Collections.Count == 0)
            {
                return;
            }

            //Finally fill out the TriplesDone for each Collection
            foreach (KeyValuePair <INode, OutputRdfCollection> kvp in context.Collections)
            {
                OutputRdfCollection c = kvp.Value;
                if (c.IsExplicit)
                {
                    foreach (Triple t in c.Triples)
                    {
                        context.TriplesDone.Add(t);
                    }
                }
                else
                {
                    INode temp = kvp.Key;
                    for (int i = 0; i < c.Triples.Count; i++)
                    {
                        context.TriplesDone.Add(c.Triples[i]);
                        if (i < c.Triples.Count - 1)
                        {
                            context.TriplesDone.Add(new Triple(c.Triples[i].Subject, rest, c.Triples[i + 1].Subject));
                        }
                        else
                        {
                            context.TriplesDone.Add(new Triple(c.Triples[i].Subject, rest, nil));
                        }
                    }
                }
            }

            //As a final sanity check look for any Explicit Collection Key which is used more than once
            cs = context.Collections.ToList();
            foreach (KeyValuePair <INode, OutputRdfCollection> kvp in cs)
            {
                OutputRdfCollection c = kvp.Value;
                if (c.IsExplicit)
                {
                    int mentions = context.Graph.GetTriples(kvp.Key).Where(t => !context.TriplesDone.Contains(t)).Count();
                    if (mentions - 1 > c.Triples.Count)
                    {
                        context.Collections.Remove(kvp.Key);
                    }
                }
            }
        }
Exemple #3
0
        private void GenerateSubjectOutput(RdfXmlWriterContext context, List <Triple> ts, bool allowRdfDescription)
        {
            // If nothing to do return
            if (ts.Count == 0)
            {
                return;
            }

            context.NamespaceMap.IncrementNesting();

            // First off determine what the XML Element should be
            // If there is a rdf:type triple then create a typed node
            // Otherwise create a rdf:Description node
            INode  rdfType    = context.Graph.CreateUriNode(UriFactory.Create(RdfSpecsHelper.RdfType));
            Triple typeTriple = ts.FirstOrDefault(t => t.Predicate.Equals(rdfType) && t.Object.NodeType == NodeType.Uri);
            INode  subj;

            if (typeTriple != null)
            {
                // Create Typed Node
                subj = typeTriple.Subject;

                // Generate the Type Reference creating a temporary namespace if necessary
                UriRefType outType;
                IUriNode   typeNode = (IUriNode)typeTriple.Object;
                String     uriref   = GenerateUriRef(context, typeNode.Uri, UriRefType.QName, out outType);
                if (outType != UriRefType.QName)
                {
                    // Need to generate a temporary namespace and try generating a QName again
                    String tempPrefix, tempUri;
                    GenerateTemporaryNamespace(context, typeNode, out tempPrefix, out tempUri);

                    uriref = GenerateUriRef(context, typeNode.Uri, UriRefType.QName, out outType);
                    if (outType != UriRefType.QName)
                    {
                        if (allowRdfDescription)
                        {
                            // Still couldn't generate a QName so fall back to rdf:Description
                            // Note that in this case we don't remove the typeTriple from those to be written as we still need to
                            // write it later
                            context.Writer.WriteStartElement("rdf", "Description", NamespaceMapper.RDF);
                        }
                        else
                        {
                            throw new RdfOutputException(WriterErrorMessages.UnreducablePropertyURIUnserializable);
                        }
                    }
                    else
                    {
                        if (uriref.Contains(':'))
                        {
                            // Type Node in relevant namespace
                            context.Writer.WriteStartElement(uriref.Substring(0, uriref.IndexOf(':')), uriref.Substring(uriref.IndexOf(':') + 1), tempUri);
                        }
                        else
                        {
                            // Type Node in default namespace
                            context.Writer.WriteStartElement(uriref);
                        }
                        ts.Remove(typeTriple);
                        context.TriplesDone.Add(typeTriple);
                    }

                    // Remember to define the temporary namespace on the current element
                    context.Writer.WriteAttributeString("xmlns", tempPrefix, null, Uri.EscapeUriString(tempUri));
                }
                else
                {
                    // Generated a valid QName
                    if (uriref.Contains(':'))
                    {
                        // Create an element with appropriate namespace
                        String ns = context.NamespaceMap.GetNamespaceUri(uriref.Substring(0, uriref.IndexOf(':'))).AbsoluteUri;
                        context.Writer.WriteStartElement(uriref.Substring(0, uriref.IndexOf(':')), uriref.Substring(uriref.IndexOf(':') + 1), ns);
                    }
                    else
                    {
                        // Create an element in default namespace
                        context.Writer.WriteStartElement(uriref);
                    }

                    context.TriplesDone.Add(typeTriple);
                    ts.Remove(typeTriple);
                }
            }
            else
            {
                subj = ts.First().Subject;
                if (allowRdfDescription)
                {
                    // Create rdf:Description Node
                    context.Writer.WriteStartElement("rdf", "Description", NamespaceMapper.RDF);
                }
                else
                {
                    throw new RdfOutputException(WriterErrorMessages.UnreducablePropertyURIUnserializable);
                }
            }

            // Always remember to add rdf:about or rdf:nodeID as appropriate
            if (subj.NodeType == NodeType.Uri)
            {
                context.Writer.WriteAttributeString("rdf", "about", NamespaceMapper.RDF, subj.ToString());//Uri.EscapeUriString(subj.ToString()));
            }
            else
            {
                // Can omit the rdf:nodeID if nesting level is > 2 i.e. not a top level subject node
                if (context.NamespaceMap.NestingLevel <= 2)
                {
                    context.Writer.WriteAttributeString("rdf", "nodeID", NamespaceMapper.RDF, context.BlankNodeMapper.GetOutputID(((IBlankNode)subj).InternalID));
                }
            }

            // If use of attributes is enabled we'll encode triples with simple literal objects
            // as attributes on the subject node directly
            if (context.UseAttributes)
            {
                // Next find any simple literals we can attach directly to the Subject Node
                List <Triple>   simpleLiterals          = new List <Triple>();
                HashSet <INode> simpleLiteralPredicates = new HashSet <INode>();
                foreach (Triple t in ts)
                {
                    if (t.Object.NodeType == NodeType.Literal)
                    {
                        ILiteralNode lit = (ILiteralNode)t.Object;
                        if (lit.DataType == null && lit.Language.Equals(String.Empty))
                        {
                            if (!simpleLiteralPredicates.Contains(t.Predicate))
                            {
                                simpleLiteralPredicates.Add(t.Predicate);
                                simpleLiterals.Add(t);
                            }
                        }
                    }
                }

                // Now go ahead and attach these to the Subject Node as attributes
                GenerateSimpleLiteralAttributes(context, simpleLiterals);
                simpleLiterals.ForEach(t => context.TriplesDone.Add(t));
                simpleLiterals.ForEach(t => ts.Remove(t));
            }

            // Then generate Predicate Output for each remaining Triple
            foreach (Triple t in ts)
            {
                GeneratePredicateOutput(context, t);
                context.TriplesDone.Add(t);
            }

            // Also check for the rare case where the subject is the key to a collection
            if (context.Collections.ContainsKey(subj))
            {
                OutputRdfCollection collection = context.Collections[subj];
                if (!collection.IsExplicit)
                {
                    GenerateCollectionItemOutput(context, collection);
                    collection.HasBeenWritten = true;
                }
            }

            context.Writer.WriteEndElement();
            context.NamespaceMap.DecrementNesting();
        }
        private void GenerateCollectionOutput(RdfXmlWriterContext context, INode key)
        {
            OutputRdfCollection c = context.Collections[key];

            if (!c.IsExplicit)
            {
                if (context.NamespaceMap.NestingLevel > 2)
                {
                    // Need to set the Predicate to have a rdf:parseType of Resource
                    context.Writer.WriteAttributeString("rdf", "parseType", NamespaceMapper.RDF, "Resource");
                }

                int length = c.Triples.Count;
                while (c.Triples.Count > 0)
                {
                    // Get the Next Item and generate the rdf:first element
                    INode next = c.Triples.First().Object;
                    c.Triples.RemoveAt(0);
                    context.NamespaceMap.IncrementNesting();
                    context.Writer.WriteStartElement("rdf", "first", NamespaceMapper.RDF);

                    // Set the value of the rdf:first Item
                    switch (next.NodeType)
                    {
                    case NodeType.Blank:
                        context.Writer.WriteAttributeString("rdf", "nodeID", NamespaceMapper.RDF, context.BlankNodeMapper.GetOutputID(((IBlankNode)next).InternalID));
                        break;

                    case NodeType.GraphLiteral:
                        throw new RdfOutputException(WriterErrorMessages.GraphLiteralsUnserializable("RDF/XML"));

                    case NodeType.Literal:
                        this.GenerateLiteralOutput(context, (ILiteralNode)next);
                        break;

                    case NodeType.Uri:
                        this.GenerateUriOutput(context, (IUriNode)next, "rdf:resource");
                        break;

                    default:
                        throw new RdfOutputException(WriterErrorMessages.UnknownNodeTypeUnserializable("RDF/XML"));
                    }

                    // Now generate the rdf:rest element
                    context.NamespaceMap.DecrementNesting();
                    context.Writer.WriteEndElement();
                    context.NamespaceMap.IncrementNesting();
                    context.Writer.WriteStartElement("rdf", "rest", NamespaceMapper.RDF);

                    if (c.Triples.Count >= 1)
                    {
                        // Set Parse Type to resource
                        context.Writer.WriteAttributeString("rdf", "parseType", NamespaceMapper.RDF, "Resource");
                    }
                    else
                    {
                        // Terminate list with an rdf:nil
                        context.Writer.WriteStartAttribute("rdf", "resource", NamespaceMapper.RDF);
                        if (context.UseDtd)
                        {
                            context.Writer.WriteRaw("&rdf;nil");
                        }
                        else
                        {
                            context.Writer.WriteRaw(NamespaceMapper.RDF + "nil");
                        }
                        context.Writer.WriteEndAttribute();
                    }
                }
                for (int i = 0; i < length; i++)
                {
                    context.NamespaceMap.DecrementNesting();
                    context.Writer.WriteEndElement();
                }
            }
            else
            {
                if (c.Triples.Count == 0)
                {
                    // Terminate the Blank Node triple by adding a rdf:nodeID attribute
                    context.Writer.WriteAttributeString("rdf", "nodeID", NamespaceMapper.RDF, context.BlankNodeMapper.GetOutputID(((IBlankNode)key).InternalID));
                }
                else
                {
                    // Need to set the Predicate to have a rdf:parseType of Resource
                    if (context.NamespaceMap.NestingLevel > 2)
                    {
                        // Need to set the Predicate to have a rdf:parseType of Resource
                        context.Writer.WriteAttributeString("rdf", "parseType", NamespaceMapper.RDF, "Resource");
                    }

                    // Output the Predicate Object list
                    while (c.Triples.Count > 0)
                    {
                        Triple t = c.Triples[0];
                        c.Triples.RemoveAt(0);
                        INode nextPred = t.Predicate;
                        INode nextObj  = t.Object;

                        // Generate the predicate
                        this.GeneratePredicateNode(context, nextPred);

                        // Output the Object
                        switch (nextObj.NodeType)
                        {
                        case NodeType.Blank:
                            if (context.Collections.ContainsKey(nextObj))
                            {
                                // Output a Collection
                                this.GenerateCollectionOutput(context, nextObj);
                            }
                            else
                            {
                                context.Writer.WriteAttributeString("rdf", "nodeID", NamespaceMapper.RDF, context.BlankNodeMapper.GetOutputID(((IBlankNode)key).InternalID));
                            }
                            break;

                        case NodeType.GraphLiteral:
                            throw new RdfOutputException(WriterErrorMessages.GraphLiteralsUnserializable("RDF/XML"));

                        case NodeType.Literal:
                            this.GenerateLiteralOutput(context, (ILiteralNode)nextObj);
                            break;

                        case NodeType.Uri:
                            this.GenerateUriOutput(context, (IUriNode)nextObj, "rdf:resource");
                            break;

                        default:
                            throw new RdfOutputException(WriterErrorMessages.UnknownNodeTypeUnserializable("RDF/XML"));
                        }

                        context.Writer.WriteEndElement();
                    }
                }
            }
        }
        /// <summary>
        /// Internal Helper method which converts a Collection into Turtle Syntax
        /// </summary>
        /// <param name="context">Writer Context</param>
        /// <param name="c">Collection to convert</param>
        /// <param name="indent">Indentation</param>
        /// <returns></returns>
        private String GenerateCollectionOutput(CompressingTurtleWriterContext context, OutputRdfCollection c, int indent)
        {
            StringBuilder output = new StringBuilder();
            bool          first  = true;

            if (!c.IsExplicit)
            {
                output.Append('(');

                while (c.Triples.Count > 0)
                {
                    if (context.PrettyPrint && !first)
                    {
                        output.Append(new String(' ', indent));
                    }
                    first = false;
                    output.Append(this.GenerateNodeOutput(context, c.Triples.First().Object, TripleSegment.Object, indent));
                    c.Triples.RemoveAt(0);
                    if (c.Triples.Count > 0)
                    {
                        output.Append(' ');
                    }
                }

                output.Append(')');
            }
            else
            {
                if (c.Triples.Count == 0)
                {
                    //Empty Collection
                    //Can represent as a single Blank Node []
                    output.Append("[]");
                }
                else
                {
                    output.Append('[');

                    while (c.Triples.Count > 0)
                    {
                        if (context.PrettyPrint && !first)
                        {
                            output.Append(new String(' ', indent));
                        }
                        first = false;
                        String temp = this.GenerateNodeOutput(context, c.Triples.First().Predicate, TripleSegment.Predicate, indent);
                        output.Append(temp);
                        output.Append(' ');
                        int addIndent;
                        if (temp.Contains('\n'))
                        {
                            addIndent = temp.Split('\n').Last().Length;
                        }
                        else
                        {
                            addIndent = temp.Length;
                        }
                        output.Append(this.GenerateNodeOutput(context, c.Triples.First().Object, TripleSegment.Object, indent + 2 + addIndent));
                        c.Triples.RemoveAt(0);

                        if (c.Triples.Count > 0)
                        {
                            output.AppendLine(" ; ");
                            output.Append(' ');
                        }
                    }

                    output.Append(']');
                }
            }
            return(output.ToString());
        }
        /// <summary>
        /// Internal Helper method which converts a Collection into Turtle Syntax
        /// </summary>
        /// <param name="context">Writer Context</param>
        /// <param name="c">Collection to convert</param>
        /// <param name="indent">Indentation</param>
        /// <returns></returns>
        private String GenerateCollectionOutput(CompressingTurtleWriterContext context, OutputRdfCollection c, int indent)
        {
            StringBuilder output = new StringBuilder();
            bool first = true;

            if (!c.IsExplicit)
            {
                output.Append('(');

                while (c.Triples.Count > 0)
                {
                    if (context.PrettyPrint && !first) output.Append(new String(' ', indent));
                    first = false;
                    output.Append(this.GenerateNodeOutput(context, c.Triples.First().Object, TripleSegment.Object, indent));
                    c.Triples.RemoveAt(0);
                    if (c.Triples.Count > 0)
                    {
                        output.Append(' ');
                    }
                }

                output.Append(')');
            }
            else
            {
                if (c.Triples.Count == 0)
                {
                    //Empty Collection
                    //Can represent as a single Blank Node []
                    output.Append("[]");
                }
                else
                {
                    output.Append('[');

                    while (c.Triples.Count > 0)
                    {
                        if (context.PrettyPrint && !first) output.Append(new String(' ', indent));
                        first = false;
                        String temp = this.GenerateNodeOutput(context, c.Triples.First().Predicate, TripleSegment.Predicate, indent);
                        output.Append(temp);
                        output.Append(' ');
                        int addIndent;
                        if (temp.Contains('\n'))
                        {
                            addIndent = temp.Split('\n').Last().Length;
                        }
                        else
                        {
                            addIndent = temp.Length;
                        }
                        output.Append(this.GenerateNodeOutput(context, c.Triples.First().Object, TripleSegment.Object, indent + 2 + addIndent));
                        c.Triples.RemoveAt(0);

                        if (c.Triples.Count > 0)
                        {
                            output.AppendLine(" ; ");
                            output.Append(' ');
                        }
                    }

                    output.Append(']');
                }
            }
            return output.ToString();
        }
Exemple #7
0
        private void GenerateCollectionOutput(RdfXmlWriterContext context, INode key, XmlElement pred, ref int nextNamespaceID, List <String> tempNamespaces, XmlDocument doc)
        {
            OutputRdfCollection c = context.Collections[key];

            c.HasBeenWritten = true;

            if (!c.IsExplicit)
            {
                XmlAttribute parseType;
                if (pred.ParentNode != doc.DocumentElement)
                {
                    //Need to set the Predicate to have a rdf:parseType of Resource
                    parseType       = doc.CreateAttribute("rdf:parseType", NamespaceMapper.RDF);
                    parseType.Value = "Resource";
                    pred.Attributes.Append(parseType);
                }

                XmlElement first, rest;
                while (c.Triples.Count > 0)
                {
                    //Get the Next Item and generate rdf:first and rdf:rest Nodes
                    INode next = c.Triples.First().Object;
                    c.Triples.RemoveAt(0);
                    first = doc.CreateElement("rdf:first", NamespaceMapper.RDF);
                    rest  = doc.CreateElement("rdf:rest", NamespaceMapper.RDF);

                    pred.AppendChild(first);
                    pred.AppendChild(rest);

                    //Set the value of the rdf:first Item
                    switch (next.NodeType)
                    {
                    case NodeType.Blank:
                        XmlAttribute nodeID = doc.CreateAttribute("rdf:nodeID", NamespaceMapper.RDF);
                        nodeID.Value = ((IBlankNode)next).InternalID;
                        first.Attributes.Append(nodeID);
                        break;

                    case NodeType.GraphLiteral:
                        throw new RdfOutputException(WriterErrorMessages.GraphLiteralsUnserializable("RDF/XML"));

                    case NodeType.Literal:
                        this.GenerateLiteralOutput(context, (ILiteralNode)next, first, doc);
                        break;

                    case NodeType.Uri:
                        this.GenerateUriOutput(context, (IUriNode)next, "rdf:resource", tempNamespaces, first, doc);
                        break;

                    default:
                        throw new RdfOutputException(WriterErrorMessages.UnknownNodeTypeUnserializable("RDF/XML"));
                    }

                    if (c.Triples.Count >= 1)
                    {
                        //Set Parse Type to resource
                        parseType       = doc.CreateAttribute("rdf:parseType", NamespaceMapper.RDF);
                        parseType.Value = "Resource";
                        rest.Attributes.Append(parseType);

                        pred = rest;
                    }
                    else
                    {
                        //Terminate list with an rdf:nil
                        XmlAttribute res = doc.CreateAttribute("rdf:resource");
                        res.InnerXml = "&rdf;nil";
                        rest.Attributes.Append(res);
                    }
                }
            }
            else
            {
                if (c.Triples.Count == 0)
                {
                    //Terminate the Blank Node triple by adding a rdf:nodeID attribute
                    XmlAttribute nodeID = doc.CreateAttribute("rdf:nodeID", NamespaceMapper.RDF);
                    nodeID.Value = ((IBlankNode)key).InternalID;
                    pred.Attributes.Append(nodeID);
                }
                else
                {
                    //Need to set the Predicate to have a rdf:parseType of Resource
                    if (pred.Name != "rdf:Description" && pred.ParentNode != doc.DocumentElement)
                    {
                        XmlAttribute parseType = doc.CreateAttribute("rdf:parseType", NamespaceMapper.RDF);
                        parseType.Value = "Resource";
                        pred.Attributes.Append(parseType);
                    }

                    //Output the Predicate Object list
                    while (c.Triples.Count > 0)
                    {
                        INode nextPred = c.Triples.First().Predicate;
                        INode nextObj  = c.Triples.First().Object;
                        c.Triples.RemoveAt(0);

                        XmlElement p;

                        //Generate the predicate
                        p = this.GeneratePredicateNode(context, nextPred, ref nextNamespaceID, tempNamespaces, doc, pred);

                        //Output the Object
                        switch (nextObj.NodeType)
                        {
                        case NodeType.Blank:
                            if (context.Collections.ContainsKey(nextObj))
                            {
                                //Output a Collection
                                this.GenerateCollectionOutput(context, nextObj, p, ref nextNamespaceID, tempNamespaces, doc);
                            }
                            else
                            {
                                XmlAttribute nodeID = doc.CreateAttribute("rdf:nodeID", NamespaceMapper.RDF);
                                nodeID.Value = ((IBlankNode)nextObj).InternalID;
                                p.Attributes.Append(nodeID);
                            }
                            break;

                        case NodeType.GraphLiteral:
                            throw new RdfOutputException(WriterErrorMessages.GraphLiteralsUnserializable("RDF/XML"));

                        case NodeType.Literal:
                            this.GenerateLiteralOutput(context, (ILiteralNode)nextObj, p, doc);
                            break;

                        case NodeType.Uri:
                            this.GenerateUriOutput(context, (IUriNode)nextObj, "rdf:resource", tempNamespaces, p, doc);
                            break;

                        default:
                            throw new RdfOutputException(WriterErrorMessages.UnknownNodeTypeUnserializable("RDF/XML"));
                        }
                    }
                }
            }
        }
Exemple #8
0
        /// <summary>
        /// Helper method which finds Collections expressed in the Graph which can be compressed into concise collection syntax constructs in some RDF syntaxes
        /// </summary>
        /// <param name="context">Writer Context</param>
        /// <param name="mode">Collection Search Mode</param>
        public static void FindCollections(ICollectionCompressingWriterContext context, CollectionSearchMode mode)
        {
            //Prepare the RDF Nodes we need
            INode first, rest, nil;
            first = context.Graph.CreateUriNode(new Uri(RdfSpecsHelper.RdfListFirst));
            rest = context.Graph.CreateUriNode(new Uri(RdfSpecsHelper.RdfListRest));
            nil = context.Graph.CreateUriNode(new Uri(RdfSpecsHelper.RdfListNil));

            //First we're going to look for implicit collections we can represent using the 
            //brackets syntax of (a b c)

            if (mode == CollectionSearchMode.All || mode == CollectionSearchMode.ImplicitOnly)
            {
                //Find all rdf:rest rdf:nil Triples
                foreach (Triple t in context.Graph.GetTriplesWithPredicateObject(rest, nil))
                {
                    //If has named list node cannot compress
                    if (t.Subject.NodeType != NodeType.Blank) break;

                    //Build the collection recursively
                    OutputRdfCollection c = new OutputRdfCollection(false);

                    //Get the thing that is the rdf:first related to this rdf:rest
                    IEnumerable<Triple> firsts = context.Graph.GetTriplesWithSubjectPredicate(t.Subject, first);//context.Graph.GetTriples(relfirstsel).Distinct();
                    Triple temp;
                    if (firsts.Count() > 1)
                    {
                        //Strange error
                        throw new RdfOutputException(WriterErrorMessages.MalformedCollectionWithMultipleFirsts);
                    }
                    else
                    {
                        //Stick this item onto the Stack
                        temp = firsts.First();
                        c.Triples.Add(temp);
                    }

                    //See if this thing is the rdf:rest of anything else
                    do
                    {
                        IEnumerable<Triple> ts = context.Graph.GetTriplesWithPredicateObject(rest, firsts.First().Subject);

                        //Stop when there isn't a rdf:rest
                        if (ts.Count() == 0)
                        {
                            break;
                        }

                        foreach (Triple t2 in ts)
                        {
                            firsts = context.Graph.GetTriplesWithSubjectPredicate(t2.Subject, first).Distinct();

                            if (firsts.Count() > 1)
                            {
                                //Strange error
                                throw new RdfOutputException(WriterErrorMessages.MalformedCollectionWithMultipleFirsts);
                            }
                            else
                            {
                                //Stick this item onto the Stack
                                temp = firsts.First();
                                //If Item is a named list node cannot compress
                                if (temp.Subject.NodeType != NodeType.Blank) break;
                                c.Triples.Add(temp);
                            }
                        }
                    } while (true);

                    //Can only compress if every List Node has a Blank Node Subject
                    if (c.Triples.All(x => x.Subject.NodeType == NodeType.Blank))
                    {
                        context.Collections.Add(firsts.First().Subject, c);
                    }
                }
            }

            //Now we want to look for explicit collections which are representable
            //using Blank Node syntax [p1 o1; p2 o2; p3 o3]
            if (mode == CollectionSearchMode.All || mode == CollectionSearchMode.ExplicitOnly)
            {
                foreach (IBlankNode b in context.Graph.Nodes.BlankNodes)
                {
                    if (context.Collections.ContainsKey(b)) continue;

                    List<Triple> ts = context.Graph.GetTriples(b).ToList();
                    ts.RemoveAll(t => t.Predicate.Equals(first));
                    ts.RemoveAll(t => t.Predicate.Equals(rest));

                    if (ts.Count <= 1)
                    {
                        //This Blank Node is only used once
                        //Add an empty explicit collection - we'll interpret this as [] later
                        context.Collections.Add(b, new OutputRdfCollection(true));
                    }
                    else
                    {
                        if (context.Graph.GetTriplesWithObject(b).Count() == 1)
                        {
                            ts.RemoveAll(t => t.Object.Equals(b));
                        }
                        OutputRdfCollection c = new OutputRdfCollection(true);
                        c.Triples.AddRange(ts);
                        context.Collections.Add(b, c);
                    }
                }
            }

            //If no collections found then no further processing
            if (context.Collections.Count == 0) return;

            //Once we've found all the Collections we need to check which are actually eligible for compression
            List<KeyValuePair<INode, OutputRdfCollection>> cs = context.Collections.ToList();

            //1 - If all the Triples pertaining to a particular Node are in the Collection then a collection is not eligible
            foreach (KeyValuePair<INode, OutputRdfCollection> kvp in cs)
            {
                OutputRdfCollection c = kvp.Value;
                if (c.IsExplicit)
                {
                    //For explicit collections if all Triples mentioning the Target Blank Node are in the Collection then can't compress
                    //If there are no Triples in the Collection then this is a single use Blank Node so can always compress
                    if (c.Triples.Count > 0 && c.Triples.Count == context.Graph.GetTriples(kvp.Key).Count())
                    {
                        context.Collections.Remove(kvp.Key);
                    }
                }
                else
                {
                    //For implicit collections if the number of Triples in the Collection is exactly ((t*3) - 1) those in the Graph then
                    //can't compress i.e. the collection is not linked to anything else
                    //Or if the number of mentions compared to the expected mentions differs by more than 1 then
                    //can't compress i.e. the collection is linked to more than one thing
                    int mentions = context.Graph.GetTriples(kvp.Key).Count();
                    int expectedMentions = ((c.Triples.Count * 3) - 1);
                    if (expectedMentions == mentions || mentions-expectedMentions != 1)
                    {
                        context.Collections.Remove(kvp.Key);
                    }
                }
            }
            if (context.Collections.Count == 0) return;

            //2 - Look for cyclic collection dependencies
            cs = context.Collections.OrderByDescending(kvp => kvp.Value.Triples.Count).ToList();

            //First build up a dependencies table
            Dictionary<INode,List<INode>> dependencies = new Dictionary<INode, List<INode>>();
            foreach (KeyValuePair<INode, OutputRdfCollection> kvp in cs)
            {
                OutputRdfCollection c = kvp.Value;

                //Empty Blank Node Collections cannot be cyclic i.e. []
                if (c.Triples.Count == 0) continue;

                //Otherwise check each Object of the Triples for other Blank Nodes
                List<INode> ds = new List<INode>();
                foreach (Triple t in c.Triples)
                {
                    //Only care about Blank Nodes which aren't the collection root but are the root for another collection
                    if (t.Object.NodeType == NodeType.Blank && !t.Object.Equals(kvp.Key) && context.Collections.ContainsKey(t.Object))
                    {
                        ds.Add(t.Object);
                    }
                }
                if (ds.Count > 0)
                {
                    dependencies.Add(kvp.Key, ds);
                }
            }

            //Now go back through that table looking for cycles
            foreach (INode n in dependencies.Keys)
            {
                List<INode> ds = dependencies[n];
                if (ds.Count == 0) continue;

                foreach (INode d in ds.ToList())
                {
                    if (dependencies.ContainsKey(d))
                    {
                        ds.AddRange(dependencies[d]);
                    }
                }

                //We can tell if there is a cycle since ds will now contain n
                if (ds.Contains(n))
                {
                    context.Collections.Remove(n);
                }
            }
            if (context.Collections.Count == 0) return;

            //Finally fill out the TriplesDone for each Collection
            foreach (KeyValuePair<INode, OutputRdfCollection> kvp in context.Collections)
            {
                OutputRdfCollection c = kvp.Value;
                if (c.IsExplicit)
                {
                    foreach (Triple t in c.Triples)
                    {
                        context.TriplesDone.Add(t);
                    }
                }
                else
                {
                    INode temp = kvp.Key;
                    for (int i = 0; i < c.Triples.Count; i++)
                    {
                        context.TriplesDone.Add(c.Triples[i]);
                        if (i < c.Triples.Count - 1)
                        {
                            context.TriplesDone.Add(new Triple(c.Triples[i].Subject, rest, c.Triples[i + 1].Subject));
                        }
                        else
                        {
                            context.TriplesDone.Add(new Triple(c.Triples[i].Subject, rest, nil));
                        }
                    }
                }
            }

            //As a final sanity check look for any Explicit Collection Key which is used more than once
            cs = context.Collections.ToList();
            foreach (KeyValuePair<INode, OutputRdfCollection> kvp in cs)
            {
                OutputRdfCollection c = kvp.Value;
                if (c.IsExplicit)
                {
                    int mentions = context.Graph.GetTriples(kvp.Key).Where(t => !context.TriplesDone.Contains(t)).Count();
                    if (mentions-1 > c.Triples.Count)
                    {
                        context.Collections.Remove(kvp.Key);
                    }
                }
            }
        }
Exemple #9
0
        private void GenerateCollectionOutput(RdfXmlWriterContext context, INode key)
        {
            OutputRdfCollection c = context.Collections[key];

            c.HasBeenWritten = true;

            if (c.IsExplicit)
            {
                if (c.Triples.Count == 0)
                {
                    //If No Triples then an isolated blank node so add rdf:nodeID and return
                    context.Writer.WriteAttributeString("rdf", "nodeID", NamespaceMapper.RDF, context.BlankNodeMapper.GetOutputID(((IBlankNode)key).InternalID));
                    return;
                }

                //First see if there is a typed triple available
                INode  rdfType    = context.Graph.CreateUriNode(UriFactory.Create(RdfSpecsHelper.RdfType));
                Triple typeTriple = c.Triples.FirstOrDefault(t => t.Predicate.Equals(rdfType) && t.Object.NodeType == NodeType.Uri);
                if (typeTriple != null)
                {
                    //Should be safe to invoke GenerateSubjectOutput but we can't allow rdf:Description
                    this.GenerateSubjectOutput(context, c.Triples, false);
                }
                else
                {
                    //Otherwise we use rdf:parseType="Resource" and invoke GeneratePredicateOutput
                    context.Writer.WriteAttributeString("rdf", "parseType", NamespaceMapper.RDF, "Resource");
                    foreach (Triple t in c.Triples)
                    {
                        this.GeneratePredicateOutput(context, t);
                        context.TriplesDone.Add(t);
                    }
                }
            }
            else
            {
                //If No Triples then use rdf:about rdf:nil and return
                if (c.Triples.Count == 0)
                {
                    context.Writer.WriteAttributeString("rdf", "about", NamespaceMapper.RDF, RdfSpecsHelper.RdfListNil);
                    return;
                }

                //Going to need rdf:parseType="Resource" on current predicate
                context.Writer.WriteAttributeString("rdf", "parseType", NamespaceMapper.RDF, "Resource");

                //Then output the elements of the Collection
                int toClose = c.Triples.Count;
                while (c.Triples.Count > 0)
                {
                    Triple t = c.Triples[0];
                    c.Triples.RemoveAt(0);

                    //rdf:first Node
                    context.Writer.WriteStartElement("rdf", "first", NamespaceMapper.RDF);
                    this.GenerateObjectOutput(context, t);

                    context.TriplesDone.Add(t);
                    context.Writer.WriteEndElement();

                    //rdf:rest Node
                    context.Writer.WriteStartElement("rdf", "rest", NamespaceMapper.RDF);
                    //context.Writer.WriteAttributeString("rdf", "parseType", NamespaceMapper.RDF, "Resource");
                }
                //Terminate the list and close all the open rdf:rest elements
                context.Writer.WriteAttributeString("rdf", "resource", NamespaceMapper.RDF, RdfSpecsHelper.RdfListNil);
                for (int i = 0; i < toClose; i++)
                {
                    context.Writer.WriteEndElement();
                }
            }
        }