<commit_before>from __future__ import generators

from rdflib import URIRef, BNode, Literal, Namespace
from rdflib import RDF, RDFS

from rdflib.Node import Node

from rdflib import plugin, exceptions

from rdflib.store import Store

from rdflib.syntax.serializer import Serializer
from rdflib.syntax.parsers import Parser
from rdflib.syntax.NamespaceManager import NamespaceManager
from rdflib import sparql
from rdflib.QueryResult import QueryResult
from rdflib.URLInputSource import URLInputSource

from xml.sax.xmlreader import InputSource
from xml.sax.saxutils import prepare_input_source
from cStringIO import StringIO
import logging
import random


class Graph(Node):
    """
    An RDF Graph.  The constructor accepts one argument, the 'store'
    that will be used to store the graph data (see the 'store'
    package for stores currently shipped with rdflib).

    Stores can be context-aware or unaware.  Unaware stores take up
    (some) less space but cannot support features that require
    context, such as true merging/demerging of sub-graphs and
    provenance.
    
    The Graph constructor can take an identifier which identifies the Graph
    by name.  If none is given, the graph is assigned a BNode for it's identifier.
    For more on named graphs, see: http://www.w3.org/2004/03/trix/
    
    Ontology for __str__ provenance terms:
    
    @prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
    @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
    @prefix : <http://rdflib.net/store/> .
    @prefix rdfg: <http://www.w3.org/2004/03/trix/rdfg-1/>.
    @prefix owl: <http://www.w3.org/2002/07/owl#>.
    @prefix log: <http://www.w3.org/2000/10/swap/log#>.
    @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
    
    :Store a owl:Class;
        rdfs:subClassOf <http://xmlns.com/wordnet/1.6/Electronic_database>;
        rdfs:subClassOf
            [a owl:Restriction;
             owl:onProperty rdfs:label;
             owl:allValuesFrom [a owl:DataRange;
                                owl:oneOf ("IOMemory"
                                           "Sleepcat"
                                           "MySQL"
                                           "Redland"
                                           "REGEXMatching"
                                           "ZODB"
                                           "AuditableStorage"
                                           "Memory")]
            ].

    :ConjunctiveGraph a owl:Class;
        rdfs:subClassOf rdfg:Graph;
        rdfs:label "The top-level graph within the store - the concatenation of all the contexts within."
        rdfs:seeAlso <http://rdflib.net/rdf_store/#ConjunctiveGraph>.
            
    :DefaultContext a owl:Class;
        rdfs:subClassOf rdfg:Graph;
        rdfs:label "The default subgraph of a conjunctive graph".
    
    
    :identifier a owl:Datatypeproperty;
        rdfs:label "The store-associated identifier of the formula. ".
        rdfs:domain log:Formula
        rdfs:range xsd:anyURI;
        
    :storage a owl:ObjectProperty;
        rdfs:domain [
            a owl:Class;
            owl:unionOf (log:Formula rdfg:Graph :ConjunctiveGraph)
        ];
        rdfs:range :Store.
        
    :default_context a owl:FunctionalProperty;
        rdfs:label "The default context for a conjunctive graph";
        rdfs:domain :ConjunctiveGraph;
        rdfs:range :DefaultContext.
        
        
    {?cg a :ConjunctiveGraph;:storage ?store}
      => {?cg owl:sameAs ?store}.
      
    {?subGraph rdfg:subGraphOf ?cg;a :DefaultContext}
      => {?cg a :ConjunctiveGraph;:default_context ?subGraphOf} .
    """

    def __init__(self, store='default', identifier=None, namespace_manager=None):
        super(Graph, self).__init__()
        self.__identifier = identifier or BNode() 
        if not isinstance(store, Store):
            # TODO: error handling
            self.__store = store = plugin.get(store, Store)()
        else:
            self.__store = store
        self.__namespace_manager = namespace_manager
        self.context_aware = False
        self.formula_aware = False

    def __get_store(self):
        return self.__store
    store = property(__get_store)

    def __get_identifier(self):
        return self.__identifier
    identifier = property(__get_identifier)

    def _get_namespace_manager(self):
        if self.__namespace_manager is None:
            self.__namespace_manager = NamespaceManager(self)
        return self.__namespace_manager
    def _set_namespace_manager(self, nm):
        self.__namespace_manager = nm
    namespace_manager = property(_get_namespace_manager, _set_namespace_manager)

    def __repr__(self):
        return "<Graph identifier=%s (%s)>" % (self.identifier, type(self))

    def __str__(self):
        if isinstance(self.identifier,URIRef):
            return "%s a rdfg:Graph;rdflib:storage [a rdflibStore [a rdflib:Store;rdfs:label '%s']]."%(self.identifier.n3(),self.store.__class__.__name__)
        else:
            return "[a rdfg:Graph;rdflib:storage [a rdflib:Store;rdfs:label '%s']]."%(self.store.__class__.__name__)

    def destroy(self, configuration):
        """
        For stores that support this functionality, it destroyes the store identified by the given configuration
        """
        if hasattr(self.__store, "destroy"):
            self.__store.destroy(configuration)

    #Transactional interfaces (optional)
    def commit(self):
        """
        Commits active transactions
        """
        if hasattr(self.__store, "commit"):
            self.__store.commit()
    
    def rollback(self):
        """
        Rollback active transactions
        """
        if hasattr(self.__store, "rollback"):
            self.__store.rollback()

    def open(self, configuration, create=True):
        """ Open the graph store.  Might be necessary for stores
        that require opening a connection to a database or acquiring some resource."""
        if hasattr(self.__store, "open"):
            self.__store.open(configuration, create)

    def close(self):
        """ Close the graph store.  Might be necessary for stores
        that require closing a connection to a database or releasing some resource."""
        if hasattr(self.__store, "close"):
            self.__store.close()

    def add(self, (s, p, o)):
        """ Add a triple, optionally provide a context.  A 3-tuple or
        rdflib.Triple can be provided.  Context must be a URIRef.  If
        no context is provides, triple is added to the default
        context."""
        self.__store.add((s, p, o), self, quoted=False)
        
    def addN(self, quads):        
        self.__store.addN([(s,p,o,c) for s,p,o,c in quads if isinstance(c,Graph) and c.identifier is self.identifier])

    def remove(self, (s, p, o)):
        """ Remove a triple from the graph.  If the triple does not
        provide a context attribute, removes the triple from all
        contexts."""

        self.__store.remove((s, p, o), context=self)

    def triples(self, (s, p, o)):
        """ Generator over the triple store.  Returns triples that
        match the given triple pattern.  If triple pattern does not
        provide a context, all contexts will be searched."""
        for (s, p, o), cg in self.__store.triples((s, p, o), context=self):
            yield (s, p, o)

    def __len__(self):
        """ Returns the number of triples in the graph. If context is specified then the number of triples in the context is returned instead."""
        return self.__store.__len__(context=self)

    def __iter__(self):
        """ Iterates over all triples in the store. """
        return self.triples((None, None, None))

    def __contains__(self, triple):
        """ Support for 'triple in graph' syntax. """
        for triple in self.triples(triple):
            return 1
        return 0

    def __hash__(self):
        return hash(self.identifier)
    def __cmp__(self, other):
        if other is None:
            return -1
        elif isinstance(other, Graph):
            return self.identifier.__cmp__(other.identifier)
        else:
            #Note if None is considered equivalent to owl:Nothing
            #Then perhaps a graph with length 0 should be considered
            #equivalent to None (if compared to it)?
            return 1


    def __iadd__(self, other):
        """ Add all triples in Graph other to Graph."""
        for triple in other:
            self.add(triple) 
        return self

    def __isub__(self, other):
        """ Subtract all triples in Graph other from Graph."""
        for triple in other:
            self.remove(triple)
        return self

    # Conv. methods

    def set(self, (subject, predicate, object)):
        """Convenience method for removing any existing triples for subject,
        predicate before adding subject, predicate, object."""
        self.remove((subject, predicate, None))
        self.add((subject, predicate, object))

    def subjects(self, predicate=None, object=None):
        """ A generator of subjects with the given predicate and object. """
        for s, p, o in self.triples((None, predicate, object)):
            yield s

    def predicates(self, subject=None, object=None):
        """ A generator of predicates with the given subject and object. """
        for s, p, o in self.triples((subject, None, object)):
            yield p

    def objects(self, subject=None, predicate=None):
        """ A generator of objects with the given subject and predicate. """
        for s, p, o in self.triples((subject, predicate, None)):
            yield o

    def subject_predicates(self, object=None):
        """ A generator of (subject, predicate) tuples for the given object """
        for s, p, o in self.triples((None, None, object)):
            yield s, p

    def subject_objects(self, predicate=None):
        """ A generator of (subject, object) tuples for the given predicate """
        for s, p, o in self.triples((None, predicate, None)):
            yield s, o

    def predicate_objects(self, subject=None):
        """ A generator of (predicate, object) tuples for the given subject """
        for s, p, o in self.triples((subject, None, None)):
            yield p, o

    def triples_choices(self, (subject, predicate, object_),context=None):
        for (s, p, o), cg in self.store.triples_choices((subject, predicate, object_), context=self):
            yield (s, p, o)

    def value(self, subject=None, predicate=RDF.value, object=None, default=None, any=False):
        """ Get a value for a subject/predicate, predicate/object, or
        subject/object pair -- exactly one of subject, predicate,
        object must be None. Useful if one knows that there may only
        be one value.

        It is one of those situations that occur a lot, hence this
        'macro' like utility

        Parameters:
        -----------
        subject, predicate, object  -- exactly one must be None
        default -- value to be returned if no values found
        any -- if True:
                 return any value in the case there is more than one
               else:
                 raise UniquenessError
        """
        retval = default
        if object is None:
            assert subject is not None
            assert predicate is not None
            values = self.objects(subject, predicate)
        if subject is None:
            assert predicate is not None
            assert object is not None
            values = self.subjects(predicate, object)
        if predicate is None:
            assert subject is not None
            assert object is not None
            values = self.predicates(subject, object)

        try:
            retval = values.next()
        except StopIteration, e:
            retval = default
        else:
            if any is False:
                try:
                    next = values.next()
                    msg = "While trying to find a value for (%s, %s, %s) the following multiple values where found:\n" % (subject, predicate, object)
                    for (s, p, o), contexts in self.store.triples((subject, predicate, object), None):
                        msg += "(%s, %s, %s)\n (contexts: %s)\n" % (s, p, o, list(contexts))
                    raise exceptions.UniquenessError(msg)
                except StopIteration, e:
                    pass
        return retval

    def label(self, subject, default=''):
        """ Queries for the RDFS.label of the subject, returns default if no label exists. """
        if subject is None:
            return default
        return self.value(subject, RDFS.label, default=default, any=True)

    def comment(self, subject, default=''):
        """ Queries for the RDFS.comment of the subject, returns default if no comment exists. """
        if subject is None:
            return default
        return self.value(subject, RDFS.comment, default=default, any=True)

    def items(self, list):
        """Generator over all items in the resource specified by list (an RDF collection)"""
        while list:
            item = self.value(list, RDF.first)
            if item:
                yield item
            list = self.value(list, RDF.rest)

    def transitive_objects(self, subject, property, remember=None):
        """ """
        if remember==None:
            remember = {}
        if not subject in remember:
            remember[subject] = 1
            yield subject
            for object in self.objects(subject, property):
                for o in self.transitive_objects(object, property, remember):
                    yield o

    def transitive_subjects(self, predicate, object, remember=None):
        """ """
        if remember==None:
            remember = {}
        if not object in remember:
            remember[object] = 1
            yield object
            for subject in self.subjects(predicate, object):
                for s in self.transitive_subjects(predicate, subject, remember):
                    yield s

    def seq(self, subject) :
        """
        Check if subject is an rdf:Seq. If yes, it returns a Seq
        class instance, None otherwise.
        """
        if (subject, RDF.type, RDF.Seq) in self :
            return Seq(self, subject)
        else :
            return None

    def qname(self, uri): 
        return self.namespace_manager.qname(uri)       

    def compute_qname(self, uri):
        return self.namespace_manager.compute_qname(uri)

    def bind(self, prefix, namespace, override=True):
        """Bind prefix to namespace. If override is True will bind namespace to given prefix if namespace was already bound to a different prefix."""
        return self.namespace_manager.bind(prefix, namespace, override=override)

    def namespaces(self):
        """Generator over all the prefix, namespace tuples.
        """
        for prefix, namespace in self.namespace_manager.namespaces():
            yield prefix, namespace

    def absolutize(self, uri, defrag=1):
        """ Will turn uri into an absolute URI if it's not one already. """
        return self.namespace_manager.absolutize(uri, defrag)

    def serialize(self, destination=None, format="xml", base=None, encoding=None):
        """ Serialize the Graph to destination. If destination is None serialize method returns the serialization as a string. Format defaults to xml (AKA rdf/xml)."""
        serializer = plugin.get(format, Serializer)(self)
        return serializer.serialize(destination, base=base, encoding=encoding)

    def prepare_input_source(self, source, publicID=None):
        if isinstance(source, InputSource):
            input_source = source
        else:
            if hasattr(source, "read") and not isinstance(source, Namespace): # we need to make sure it's not an instance of Namespace since Namespace instances have a read attr
                input_source = prepare_input_source(source)
            else:
                location = self.absolutize(source)
                input_source = URLInputSource(location)
                publicID = publicID or location
        if publicID:
            input_source.setPublicId(publicID)
        id = input_source.getPublicId()
        if id is None:
            logging.info("no publicID set for source. Using '' for publicID.")
            input_source.setPublicId("")
        return input_source

    def parse(self, source, publicID=None, format="xml", **args):
        """ Parse source into Graph. If Graph is context-aware it'll get loaded into it's own context (sub graph). Format defaults to xml (AKA rdf/xml). The publicID argument is for specifying the logical URI for the case that it's different from the physical source URI. Returns the context into which the source was parsed."""
        source = self.prepare_input_source(source, publicID)
        parser = plugin.get(format, Parser)()
        parser.parse(source, self, **args)
        return self

    def load(self, source, publicID=None, format="xml"):
        self.parse(source, publicID, format)

    def n3(self):
        """return an n3 identifier for the Graph"""
        return "[%s]" % self.identifier.n3()

    def __reduce__(self):
        return (Graph, (self.store, self.identifier,))

#     def __getstate__(self):
#         return False

    def isomorphic(self, other):
        # TODO: this is only an approximation.
        if len(self)!=len(other):
            return False
        for s, p, o in self:
            if not isinstance(s, BNode) and not isinstance(o, BNode):
                if not (s, p, o) in other:
                    return False
        for s, p, o in other:
            if not isinstance(s, BNode) and not isinstance(o, BNode):
                if not (s, p, o) in self:
                    return False
        # TODO: very well could be a false positive at this point yet.
        return True

    def connected(self):
        """ Check if the Graph is connected (the Graph is considered undirectional).
        
        Performs a search on the Graph, starting from a random node.
        Then iteratively goes depth-first through the triplets where the node is subject and object.
        Returns True if all nodes have been visited and False if it cannot continue and there are still unvisited nodes left.
        """
        all_nodes = list(self.all_nodes())
        discovered = []
        # Take a random one, could also always take the first one, doesn't really matter.
        visiting = [all_nodes[random.randrange(len(all_nodes))]]
        while visiting:
            x = visiting.pop()
            if x not in discovered:
                discovered.append(x)
            for newX in self.objects(subject=x):
                if newX not in discovered and newX not in visiting:
                    visiting.append(newX)
            for newX in self.subjects(object=x):
                if newX not in discovered and newX not in visiting:
                    visiting.append(newX)
        # Optimisation by only considering length, since no new objects can be introduced anywhere.
        if len(all_nodes) == len(discovered):
            return True
        else:
            return False

    def all_nodes(self):
        obj = set(self.objects())
        allNodes = obj.union(set(self.subjects()))
        return allNodes

class ConjunctiveGraph(Graph): # AKA ConjunctiveGraph

    def __init__(self, store='default', identifier=None):
        super(ConjunctiveGraph, self).__init__(store)
        assert self.store.context_aware, "ConjunctiveGraph must be backed by a context aware store."
        self.context_aware = True
        self.default_context = Graph(store=self.store, identifier=identifier or BNode())

    def __str__(self):
        return "[a rdflib:DefaultContext] rdfg:subGraphOf [a rdfg:Graph;rdflib:storage [a rdflib:Store;rdfs:label '%s']]"%(self.store.__class__.__name__)

    def add(self, (s, p, o), context=None):
        """"A conjunctive graph adds to its default context."""
        self.store.add((s, p, o), context=context or self.default_context, quoted=False)

    def addN(self, quads):        
        self.store.addN(quads)    

    def remove(self, (s, p, o), context=None):
        """A conjunctive graph removes from all its contexts."""
        self.store.remove((s, p, o), context)

    def triples(self, (s, p, o), context=None):
        """An iterator over all the triples in the entire conjunctive graph."""
        for (s, p, o), cg in self.store.triples((s, p, o), context):
            yield (s, p, o), cg

    def triples_choices(self, (s, p, o), context=None):
        """An iterator over all the triples in the entire conjunctive graph."""        
        for (s1, p1, o1), cg in self.store.triples_choices((s, p, o), context):
            yield (s1, p1, o1), cg

    def value(self, subject=None, predicate=RDF.value, object=None, default=None, any=False, context=None):
        """ Get a value for a subject/predicate, predicate/object, or
        subject/object pair -- exactly one of subject, predicate,
        object must be None. Useful if one knows that there may only
        be one value.

        It is one of those situations that occur a lot, hence this
        'macro' like utility

        Parameters:
        -----------
        subject, predicate, object  -- exactly one must be None
        default -- value to be returned if no values found
        any -- if True:
                 return any value in the case there is more than one
               else:
                 raise UniquenessError
        """
        retval = default
        if object is None:
            assert subject is not None
            assert predicate is not None
            values = self.objects(subject, predicate, context)
        if subject is None:
            assert predicate is not None
            assert object is not None
            values = self.subjects(predicate, object, context)
        if predicate is None:
            assert subject is not None
            assert object is not None
            values = self.predicates(subject, object, context)

        try:
            retval = values.next()
        except StopIteration, e:
            retval = default
        else:
            if any is False:
                try:
                    next = values.next()
                    msg = "While trying to find a value for ((%s, %s, %s) %s) the following multiple values where found:\n" % (subject, predicate, object, context)
                    for (s, p, o), contexts in self.store.triples((subject, predicate, object), context):
                        msg += "(%s, %s, %s)\n (contexts: %s)\n" % (s, p, o, list(contexts))
                    raise exceptions.UniquenessError(msg)
                except StopIteration, e:
                    pass
        return retval

    def subjects(self, predicate=None, object=None, context=None):
        """ A generator of subjects with the given predicate and object. """
        for (s, p, o), cg in self.triples((None, predicate, object), context):
            yield s

    def predicates(self, subject=None, object=None, context=None):
        """ A generator of predicates with the given subject and object. """
        for (s, p, o), cg in self.triples((subject, None, object), context):
            yield p

    def objects(self, subject=None, predicate=None, context=None):
        """ A generator of objects with the given subject and predicate. """
        for (s, p, o), cg in self.triples((subject, predicate, None), context):
            yield o

    def subject_predicates(self, object=None, context=None):
        """ A generator of (subject, predicate) tuples for the given object """
        for (s, p, o), cg in self.triples((None, None, object), context):
            yield s, p

    def subject_objects(self, predicate=None, context=None):
        """ A generator of (subject, object) tuples for the given predicate """
        for (s, p, o), cg in self.triples((None, predicate, None), context):
            yield s, o

    def predicate_objects(self, subject=None, contex=None):
        """ A generator of (predicate, object) tuples for the given subject """
        for (s, p, o), cg in self.triples((subject, None, None), context):
            yield p, o

    def __len__(self, context=None):
        """Returns the number of triples in the entire conjunctive graph."""
        return self.store.__len__(context)

    def contexts(self, triple=None):
        """ 
        Iterator over all contexts in the graph. If triple is
        specified, a generator over all contexts the triple is in.
        """
        for context in self.store.contexts(triple):
            yield context

    def remove_context(self, context):
        """ Removes the given context from the graph. """
        self.store.remove((None, None, None), context)

    def context_id(self, uri, context_id=None):
        """ URI#context """
        uri = uri.split("#", 1)[0]
        if context_id is None:
            context_id = "#context"
        return URIRef(context_id, base=uri)

    def parse(self, source, publicID=None, format="xml", **args):
        """ 
        Parse source into Graph into it's own context (sub
        graph). Format defaults to xml (AKA rdf/xml). The publicID
        argument is for specifying the logical URI for the case that
        it's different from the physical source URI. Returns the
        context into which the source was parsed. In the case of n3 it
        returns the root context.
        """
        source = self.prepare_input_source(source, publicID)
        id = self.context_id(self.absolutize(source.getPublicId()))
        context = Graph(store=self.store, identifier=id)
        context.remove((None, None, None))
        context.parse(source, publicID=publicID, format=format, **args)
        return context

    def query(self, strOrQuery, initBindings={}, initNs={}, DEBUG=False, processor="bison"):
        """
        Executes a SPARQL query against this Conjunctive Graph
        strOrQuery - Is either a string consisting of the SPARQL query or an instance of rdflib.sparql.bison.Query.Query
        initBindings - A mapping from variable name to an RDFLib term (used for initial bindings for SPARQL query)
        initNS - A mapping from a namespace prefix to an instance of rdflib.Namespace (used for SPARQL query)
        DEBUG - A boolean flag passed on to the SPARQL parser and evaluation engine
        FIXME: This should probably take as a parameter a result type (SPARQL XML,JSON,Python lists/tuples)
        """
        p = plugin.get(processor, sparql.Processor)(self.store)
        return plugin.get('SPARQLQueryResult',QueryResult)(p.query(strOrQuery, initBindings, initNs, DEBUG))

    def __reduce__(self):
        return (ConjunctiveGraph, (self.store, self.identifier,))


class QuotedGraph(Graph):

    def __init__(self, store, identifier):
        super(QuotedGraph, self).__init__(store, identifier)<commit_msg>moved query method from CG to G<commit_after>from __future__ import generators

from rdflib import URIRef, BNode, Literal, Namespace
from rdflib import RDF, RDFS

from rdflib.Node import Node

from rdflib import plugin, exceptions

from rdflib.store import Store

from rdflib.syntax.serializer import Serializer
from rdflib.syntax.parsers import Parser
from rdflib.syntax.NamespaceManager import NamespaceManager
from rdflib import sparql
from rdflib.QueryResult import QueryResult
from rdflib.URLInputSource import URLInputSource

from xml.sax.xmlreader import InputSource
from xml.sax.saxutils import prepare_input_source
from cStringIO import StringIO
import logging
import random


class Graph(Node):
    """
    An RDF Graph.  The constructor accepts one argument, the 'store'
    that will be used to store the graph data (see the 'store'
    package for stores currently shipped with rdflib).

    Stores can be context-aware or unaware.  Unaware stores take up
    (some) less space but cannot support features that require
    context, such as true merging/demerging of sub-graphs and
    provenance.
    
    The Graph constructor can take an identifier which identifies the Graph
    by name.  If none is given, the graph is assigned a BNode for it's identifier.
    For more on named graphs, see: http://www.w3.org/2004/03/trix/
    
    Ontology for __str__ provenance terms:
    
    @prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
    @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
    @prefix : <http://rdflib.net/store/> .
    @prefix rdfg: <http://www.w3.org/2004/03/trix/rdfg-1/>.
    @prefix owl: <http://www.w3.org/2002/07/owl#>.
    @prefix log: <http://www.w3.org/2000/10/swap/log#>.
    @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
    
    :Store a owl:Class;
        rdfs:subClassOf <http://xmlns.com/wordnet/1.6/Electronic_database>;
        rdfs:subClassOf
            [a owl:Restriction;
             owl:onProperty rdfs:label;
             owl:allValuesFrom [a owl:DataRange;
                                owl:oneOf ("IOMemory"
                                           "Sleepcat"
                                           "MySQL"
                                           "Redland"
                                           "REGEXMatching"
                                           "ZODB"
                                           "AuditableStorage"
                                           "Memory")]
            ].

    :ConjunctiveGraph a owl:Class;
        rdfs:subClassOf rdfg:Graph;
        rdfs:label "The top-level graph within the store - the concatenation of all the contexts within."
        rdfs:seeAlso <http://rdflib.net/rdf_store/#ConjunctiveGraph>.
            
    :DefaultContext a owl:Class;
        rdfs:subClassOf rdfg:Graph;
        rdfs:label "The default subgraph of a conjunctive graph".
    
    
    :identifier a owl:Datatypeproperty;
        rdfs:label "The store-associated identifier of the formula. ".
        rdfs:domain log:Formula
        rdfs:range xsd:anyURI;
        
    :storage a owl:ObjectProperty;
        rdfs:domain [
            a owl:Class;
            owl:unionOf (log:Formula rdfg:Graph :ConjunctiveGraph)
        ];
        rdfs:range :Store.
        
    :default_context a owl:FunctionalProperty;
        rdfs:label "The default context for a conjunctive graph";
        rdfs:domain :ConjunctiveGraph;
        rdfs:range :DefaultContext.
        
        
    {?cg a :ConjunctiveGraph;:storage ?store}
      => {?cg owl:sameAs ?store}.
      
    {?subGraph rdfg:subGraphOf ?cg;a :DefaultContext}
      => {?cg a :ConjunctiveGraph;:default_context ?subGraphOf} .
    """

    def __init__(self, store='default', identifier=None, namespace_manager=None):
        super(Graph, self).__init__()
        self.__identifier = identifier or BNode() 
        if not isinstance(store, Store):
            # TODO: error handling
            self.__store = store = plugin.get(store, Store)()
        else:
            self.__store = store
        self.__namespace_manager = namespace_manager
        self.context_aware = False
        self.formula_aware = False

    def __get_store(self):
        return self.__store
    store = property(__get_store)

    def __get_identifier(self):
        return self.__identifier
    identifier = property(__get_identifier)

    def _get_namespace_manager(self):
        if self.__namespace_manager is None:
            self.__namespace_manager = NamespaceManager(self)
        return self.__namespace_manager
    def _set_namespace_manager(self, nm):
        self.__namespace_manager = nm
    namespace_manager = property(_get_namespace_manager, _set_namespace_manager)

    def __repr__(self):
        return "<Graph identifier=%s (%s)>" % (self.identifier, type(self))

    def __str__(self):
        if isinstance(self.identifier,URIRef):
            return "%s a rdfg:Graph;rdflib:storage [a rdflibStore [a rdflib:Store;rdfs:label '%s']]."%(self.identifier.n3(),self.store.__class__.__name__)
        else:
            return "[a rdfg:Graph;rdflib:storage [a rdflib:Store;rdfs:label '%s']]."%(self.store.__class__.__name__)

    def destroy(self, configuration):
        """
        For stores that support this functionality, it destroyes the store identified by the given configuration
        """
        if hasattr(self.__store, "destroy"):
            self.__store.destroy(configuration)

    #Transactional interfaces (optional)
    def commit(self):
        """
        Commits active transactions
        """
        if hasattr(self.__store, "commit"):
            self.__store.commit()
    
    def rollback(self):
        """
        Rollback active transactions
        """
        if hasattr(self.__store, "rollback"):
            self.__store.rollback()

    def open(self, configuration, create=True):
        """ Open the graph store.  Might be necessary for stores
        that require opening a connection to a database or acquiring some resource."""
        if hasattr(self.__store, "open"):
            self.__store.open(configuration, create)

    def close(self):
        """ Close the graph store.  Might be necessary for stores
        that require closing a connection to a database or releasing some resource."""
        if hasattr(self.__store, "close"):
            self.__store.close()

    def add(self, (s, p, o)):
        """ Add a triple, optionally provide a context.  A 3-tuple or
        rdflib.Triple can be provided.  Context must be a URIRef.  If
        no context is provides, triple is added to the default
        context."""
        self.__store.add((s, p, o), self, quoted=False)
        
    def addN(self, quads):        
        self.__store.addN([(s,p,o,c) for s,p,o,c in quads if isinstance(c,Graph) and c.identifier is self.identifier])

    def remove(self, (s, p, o)):
        """ Remove a triple from the graph.  If the triple does not
        provide a context attribute, removes the triple from all
        contexts."""

        self.__store.remove((s, p, o), context=self)

    def triples(self, (s, p, o)):
        """ Generator over the triple store.  Returns triples that
        match the given triple pattern.  If triple pattern does not
        provide a context, all contexts will be searched."""
        for (s, p, o), cg in self.__store.triples((s, p, o), context=self):
            yield (s, p, o)

    def __len__(self):
        """ Returns the number of triples in the graph. If context is specified then the number of triples in the context is returned instead."""
        return self.__store.__len__(context=self)

    def __iter__(self):
        """ Iterates over all triples in the store. """
        return self.triples((None, None, None))

    def __contains__(self, triple):
        """ Support for 'triple in graph' syntax. """
        for triple in self.triples(triple):
            return 1
        return 0

    def __hash__(self):
        return hash(self.identifier)
    def __cmp__(self, other):
        if other is None:
            return -1
        elif isinstance(other, Graph):
            return self.identifier.__cmp__(other.identifier)
        else:
            #Note if None is considered equivalent to owl:Nothing
            #Then perhaps a graph with length 0 should be considered
            #equivalent to None (if compared to it)?
            return 1


    def __iadd__(self, other):
        """ Add all triples in Graph other to Graph."""
        for triple in other:
            self.add(triple) 
        return self

    def __isub__(self, other):
        """ Subtract all triples in Graph other from Graph."""
        for triple in other:
            self.remove(triple)
        return self

    # Conv. methods

    def set(self, (subject, predicate, object)):
        """Convenience method for removing any existing triples for subject,
        predicate before adding subject, predicate, object."""
        self.remove((subject, predicate, None))
        self.add((subject, predicate, object))

    def subjects(self, predicate=None, object=None):
        """ A generator of subjects with the given predicate and object. """
        for s, p, o in self.triples((None, predicate, object)):
            yield s

    def predicates(self, subject=None, object=None):
        """ A generator of predicates with the given subject and object. """
        for s, p, o in self.triples((subject, None, object)):
            yield p

    def objects(self, subject=None, predicate=None):
        """ A generator of objects with the given subject and predicate. """
        for s, p, o in self.triples((subject, predicate, None)):
            yield o

    def subject_predicates(self, object=None):
        """ A generator of (subject, predicate) tuples for the given object """
        for s, p, o in self.triples((None, None, object)):
            yield s, p

    def subject_objects(self, predicate=None):
        """ A generator of (subject, object) tuples for the given predicate """
        for s, p, o in self.triples((None, predicate, None)):
            yield s, o

    def predicate_objects(self, subject=None):
        """ A generator of (predicate, object) tuples for the given subject """
        for s, p, o in self.triples((subject, None, None)):
            yield p, o

    def triples_choices(self, (subject, predicate, object_),context=None):
        for (s, p, o), cg in self.store.triples_choices((subject, predicate, object_), context=self):
            yield (s, p, o)

    def value(self, subject=None, predicate=RDF.value, object=None, default=None, any=False):
        """ Get a value for a subject/predicate, predicate/object, or
        subject/object pair -- exactly one of subject, predicate,
        object must be None. Useful if one knows that there may only
        be one value.

        It is one of those situations that occur a lot, hence this
        'macro' like utility

        Parameters:
        -----------
        subject, predicate, object  -- exactly one must be None
        default -- value to be returned if no values found
        any -- if True:
                 return any value in the case there is more than one
               else:
                 raise UniquenessError
        """
        retval = default
        if object is None:
            assert subject is not None
            assert predicate is not None
            values = self.objects(subject, predicate)
        if subject is None:
            assert predicate is not None
            assert object is not None
            values = self.subjects(predicate, object)
        if predicate is None:
            assert subject is not None
            assert object is not None
            values = self.predicates(subject, object)

        try:
            retval = values.next()
        except StopIteration, e:
            retval = default
        else:
            if any is False:
                try:
                    next = values.next()
                    msg = "While trying to find a value for (%s, %s, %s) the following multiple values where found:\n" % (subject, predicate, object)
                    for (s, p, o), contexts in self.store.triples((subject, predicate, object), None):
                        msg += "(%s, %s, %s)\n (contexts: %s)\n" % (s, p, o, list(contexts))
                    raise exceptions.UniquenessError(msg)
                except StopIteration, e:
                    pass
        return retval

    def label(self, subject, default=''):
        """ Queries for the RDFS.label of the subject, returns default if no label exists. """
        if subject is None:
            return default
        return self.value(subject, RDFS.label, default=default, any=True)

    def comment(self, subject, default=''):
        """ Queries for the RDFS.comment of the subject, returns default if no comment exists. """
        if subject is None:
            return default
        return self.value(subject, RDFS.comment, default=default, any=True)

    def items(self, list):
        """Generator over all items in the resource specified by list (an RDF collection)"""
        while list:
            item = self.value(list, RDF.first)
            if item:
                yield item
            list = self.value(list, RDF.rest)

    def transitive_objects(self, subject, property, remember=None):
        """ """
        if remember==None:
            remember = {}
        if not subject in remember:
            remember[subject] = 1
            yield subject
            for object in self.objects(subject, property):
                for o in self.transitive_objects(object, property, remember):
                    yield o

    def transitive_subjects(self, predicate, object, remember=None):
        """ """
        if remember==None:
            remember = {}
        if not object in remember:
            remember[object] = 1
            yield object
            for subject in self.subjects(predicate, object):
                for s in self.transitive_subjects(predicate, subject, remember):
                    yield s

    def seq(self, subject) :
        """
        Check if subject is an rdf:Seq. If yes, it returns a Seq
        class instance, None otherwise.
        """
        if (subject, RDF.type, RDF.Seq) in self :
            return Seq(self, subject)
        else :
            return None

    def qname(self, uri): 
        return self.namespace_manager.qname(uri)       

    def compute_qname(self, uri):
        return self.namespace_manager.compute_qname(uri)

    def bind(self, prefix, namespace, override=True):
        """Bind prefix to namespace. If override is True will bind namespace to given prefix if namespace was already bound to a different prefix."""
        return self.namespace_manager.bind(prefix, namespace, override=override)

    def namespaces(self):
        """Generator over all the prefix, namespace tuples.
        """
        for prefix, namespace in self.namespace_manager.namespaces():
            yield prefix, namespace

    def absolutize(self, uri, defrag=1):
        """ Will turn uri into an absolute URI if it's not one already. """
        return self.namespace_manager.absolutize(uri, defrag)

    def serialize(self, destination=None, format="xml", base=None, encoding=None):
        """ Serialize the Graph to destination. If destination is None serialize method returns the serialization as a string. Format defaults to xml (AKA rdf/xml)."""
        serializer = plugin.get(format, Serializer)(self)
        return serializer.serialize(destination, base=base, encoding=encoding)

    def prepare_input_source(self, source, publicID=None):
        if isinstance(source, InputSource):
            input_source = source
        else:
            if hasattr(source, "read") and not isinstance(source, Namespace): # we need to make sure it's not an instance of Namespace since Namespace instances have a read attr
                input_source = prepare_input_source(source)
            else:
                location = self.absolutize(source)
                input_source = URLInputSource(location)
                publicID = publicID or location
        if publicID:
            input_source.setPublicId(publicID)
        id = input_source.getPublicId()
        if id is None:
            logging.info("no publicID set for source. Using '' for publicID.")
            input_source.setPublicId("")
        return input_source

    def parse(self, source, publicID=None, format="xml", **args):
        """ Parse source into Graph. If Graph is context-aware it'll get loaded into it's own context (sub graph). Format defaults to xml (AKA rdf/xml). The publicID argument is for specifying the logical URI for the case that it's different from the physical source URI. Returns the context into which the source was parsed."""
        source = self.prepare_input_source(source, publicID)
        parser = plugin.get(format, Parser)()
        parser.parse(source, self, **args)
        return self

    def load(self, source, publicID=None, format="xml"):
        self.parse(source, publicID, format)

    def query(self, strOrQuery, initBindings={}, initNs={}, DEBUG=False, processor="bison"):
        """
        Executes a SPARQL query against this Conjunctive Graph
        strOrQuery - Is either a string consisting of the SPARQL query or an instance of rdflib.sparql.bison.Query.Query
        initBindings - A mapping from variable name to an RDFLib term (used for initial bindings for SPARQL query)
        initNS - A mapping from a namespace prefix to an instance of rdflib.Namespace (used for SPARQL query)
        DEBUG - A boolean flag passed on to the SPARQL parser and evaluation engine
        FIXME: This should probably take as a parameter a result type (SPARQL XML,JSON,Python lists/tuples)
        """
        p = plugin.get(processor, sparql.Processor)(self.store)
        return plugin.get('SPARQLQueryResult',QueryResult)(p.query(strOrQuery, initBindings, initNs, DEBUG))

    def n3(self):
        """return an n3 identifier for the Graph"""
        return "[%s]" % self.identifier.n3()

    def __reduce__(self):
        return (Graph, (self.store, self.identifier,))

#     def __getstate__(self):
#         return False

    def isomorphic(self, other):
        # TODO: this is only an approximation.
        if len(self)!=len(other):
            return False
        for s, p, o in self:
            if not isinstance(s, BNode) and not isinstance(o, BNode):
                if not (s, p, o) in other:
                    return False
        for s, p, o in other:
            if not isinstance(s, BNode) and not isinstance(o, BNode):
                if not (s, p, o) in self:
                    return False
        # TODO: very well could be a false positive at this point yet.
        return True

    def connected(self):
        """ Check if the Graph is connected (the Graph is considered undirectional).
        
        Performs a search on the Graph, starting from a random node.
        Then iteratively goes depth-first through the triplets where the node is subject and object.
        Returns True if all nodes have been visited and False if it cannot continue and there are still unvisited nodes left.
        """
        all_nodes = list(self.all_nodes())
        discovered = []
        # Take a random one, could also always take the first one, doesn't really matter.
        visiting = [all_nodes[random.randrange(len(all_nodes))]]
        while visiting:
            x = visiting.pop()
            if x not in discovered:
                discovered.append(x)
            for newX in self.objects(subject=x):
                if newX not in discovered and newX not in visiting:
                    visiting.append(newX)
            for newX in self.subjects(object=x):
                if newX not in discovered and newX not in visiting:
                    visiting.append(newX)
        # Optimisation by only considering length, since no new objects can be introduced anywhere.
        if len(all_nodes) == len(discovered):
            return True
        else:
            return False

    def all_nodes(self):
        obj = set(self.objects())
        allNodes = obj.union(set(self.subjects()))
        return allNodes

class ConjunctiveGraph(Graph): # AKA ConjunctiveGraph

    def __init__(self, store='default', identifier=None):
        super(ConjunctiveGraph, self).__init__(store)
        assert self.store.context_aware, "ConjunctiveGraph must be backed by a context aware store."
        self.context_aware = True
        self.default_context = Graph(store=self.store, identifier=identifier or BNode())

    def __str__(self):
        return "[a rdflib:DefaultContext] rdfg:subGraphOf [a rdfg:Graph;rdflib:storage [a rdflib:Store;rdfs:label '%s']]"%(self.store.__class__.__name__)

    def add(self, (s, p, o), context=None):
        """"A conjunctive graph adds to its default context."""
        self.store.add((s, p, o), context=context or self.default_context, quoted=False)

    def addN(self, quads):        
        self.store.addN(quads)    

    def remove(self, (s, p, o), context=None):
        """A conjunctive graph removes from all its contexts."""
        self.store.remove((s, p, o), context)

    def triples(self, (s, p, o), context=None):
        """An iterator over all the triples in the entire conjunctive graph."""
        for (s, p, o), cg in self.store.triples((s, p, o), context):
            yield (s, p, o), cg

    def triples_choices(self, (s, p, o), context=None):
        """An iterator over all the triples in the entire conjunctive graph."""        
        for (s1, p1, o1), cg in self.store.triples_choices((s, p, o), context):
            yield (s1, p1, o1), cg

    def value(self, subject=None, predicate=RDF.value, object=None, default=None, any=False, context=None):
        """ Get a value for a subject/predicate, predicate/object, or
        subject/object pair -- exactly one of subject, predicate,
        object must be None. Useful if one knows that there may only
        be one value.

        It is one of those situations that occur a lot, hence this
        'macro' like utility

        Parameters:
        -----------
        subject, predicate, object  -- exactly one must be None
        default -- value to be returned if no values found
        any -- if True:
                 return any value in the case there is more than one
               else:
                 raise UniquenessError
        """
        retval = default
        if object is None:
            assert subject is not None
            assert predicate is not None
            values = self.objects(subject, predicate, context)
        if subject is None:
            assert predicate is not None
            assert object is not None
            values = self.subjects(predicate, object, context)
        if predicate is None:
            assert subject is not None
            assert object is not None
            values = self.predicates(subject, object, context)

        try:
            retval = values.next()
        except StopIteration, e:
            retval = default
        else:
            if any is False:
                try:
                    next = values.next()
                    msg = "While trying to find a value for ((%s, %s, %s) %s) the following multiple values where found:\n" % (subject, predicate, object, context)
                    for (s, p, o), contexts in self.store.triples((subject, predicate, object), context):
                        msg += "(%s, %s, %s)\n (contexts: %s)\n" % (s, p, o, list(contexts))
                    raise exceptions.UniquenessError(msg)
                except StopIteration, e:
                    pass
        return retval

    def subjects(self, predicate=None, object=None, context=None):
        """ A generator of subjects with the given predicate and object. """
        for (s, p, o), cg in self.triples((None, predicate, object), context):
            yield s

    def predicates(self, subject=None, object=None, context=None):
        """ A generator of predicates with the given subject and object. """
        for (s, p, o), cg in self.triples((subject, None, object), context):
            yield p

    def objects(self, subject=None, predicate=None, context=None):
        """ A generator of objects with the given subject and predicate. """
        for (s, p, o), cg in self.triples((subject, predicate, None), context):
            yield o

    def subject_predicates(self, object=None, context=None):
        """ A generator of (subject, predicate) tuples for the given object """
        for (s, p, o), cg in self.triples((None, None, object), context):
            yield s, p

    def subject_objects(self, predicate=None, context=None):
        """ A generator of (subject, object) tuples for the given predicate """
        for (s, p, o), cg in self.triples((None, predicate, None), context):
            yield s, o

    def predicate_objects(self, subject=None, contex=None):
        """ A generator of (predicate, object) tuples for the given subject """
        for (s, p, o), cg in self.triples((subject, None, None), context):
            yield p, o

    def __len__(self, context=None):
        """Returns the number of triples in the entire conjunctive graph."""
        return self.store.__len__(context)

    def contexts(self, triple=None):
        """ 
        Iterator over all contexts in the graph. If triple is
        specified, a generator over all contexts the triple is in.
        """
        for context in self.store.contexts(triple):
            yield context

    def remove_context(self, context):
        """ Removes the given context from the graph. """
        self.store.remove((None, None, None), context)

    def context_id(self, uri, context_id=None):
        """ URI#context """
        uri = uri.split("#", 1)[0]
        if context_id is None:
            context_id = "#context"
        return URIRef(context_id, base=uri)

    def parse(self, source, publicID=None, format="xml", **args):
        """ 
        Parse source into Graph into it's own context (sub
        graph). Format defaults to xml (AKA rdf/xml). The publicID
        argument is for specifying the logical URI for the case that
        it's different from the physical source URI. Returns the
        context into which the source was parsed. In the case of n3 it
        returns the root context.
        """
        source = self.prepare_input_source(source, publicID)
        id = self.context_id(self.absolutize(source.getPublicId()))
        context = Graph(store=self.store, identifier=id)
        context.remove((None, None, None))
        context.parse(source, publicID=publicID, format=format, **args)
        return context

    def __reduce__(self):
        return (ConjunctiveGraph, (self.store, self.identifier,))


class QuotedGraph(Graph):

    def __init__(self, store, identifier):
        super(QuotedGraph, self).__init__(store, identifier)
{'fraction_non_alphanumeric': 0.09514491415912867, 'fraction_numerical': 0.002732139560642422, 'mean_word_length': 2.239311128386055, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 14, 'xml': 28}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1495938', 'n_tokens_mistral': 14941, 'n_tokens_neox': 14326, 'n_words': 5227}