#!/usr/bin/env python
# -*- coding: utf-8 -*-
# pylint: disable-msg=C0103
################################################################################
# Copyright (c) 2006-2017 Franz Inc.  
# All rights reserved. This program and the accompanying materials are
# made available under the terms of the MIT License which accompanies
# this distribution, and is available at http://opensource.org/licenses/MIT
################################################################################
from __future__ import absolute_import
from __future__ import with_statement
from __future__ import unicode_literals
from future.builtins import object
from past.builtins import map, unicode, basestring
from franz.miniclient.request import wrap_callback
from franz.openrdf.util.contexts import output_to
from .repositoryresult import RepositoryResult
from ..exceptions import IllegalOptionException, IllegalArgumentException
from ..model import Statement, Value, URI
from ..model.literal import RangeLiteral, GeoCoordinate, GeoSpatialRegion, GeoBox, GeoCircle, GeoPolygon, Literal
from ..query.dataset import ALL_CONTEXTS, MINI_NULL_CONTEXT
from ..query.query import Query, TupleQuery, UpdateQuery, GraphQuery, BooleanQuery, QueryLanguage
from ..rio.rdfformat import RDFFormat
from ..util import uris
from collections import namedtuple
import copy, sys, warnings
from contextlib import contextmanager
if sys.version_info[0] > 2:
    # Hack for isinstance checks
    import io
    file = io.IOBase
[docs]class RepositoryConnection(object):
    """
    The RepositoryConnection class is the main interface for updating data
    in and performing queries on a
    :class:`~franz.openrdf.repository.repository.Repository`. By default, a
    RespositoryConnection is in autoCommit mode, meaning that each
    operation corresponds to a single transaction on the underlying triple
    store. autoCommit can be switched off, in which case it is up to the
    user to handle transaction commit/rollback. Note that care should be
    taken to always properly close a RepositoryConnection after one is
    finished with it, to free up resources and avoid unnecessary locks.
    Note that concurrent access to the same connection object is explicitly
    forbidden. The client must perform its own synchronization to ensure
    non-concurrent access.
    Several methods take a *vararg* argument that optionally specifies a
    set of contexts on which the method should operate. (A context is
    the URI of a subgraph.) Note that a *vararg* parameter is optional, it
    can be completely left out of the method call, in which case a method
    either operates on a provided statement's context (if one of the method
    parameters is a statement or collection of statements), or operates on
    the repository as a whole, completely ignoring context. A *vararg*
    argument may also be ``None``, meaning that the method operates on
    those statements which have no associated context only.
    """
[docs]    def __init__(self, repository, close_repo=False):
        """
        Call through :meth:`~franz.openrdf.repository.repository.Repository.getConnection`.
        :param repository: Repository to connect to.
        :type repository: Repository
        :param close_repo: If ``True`` shutdown the repository when this connection is closed.
                          The default is ``False``.
        :type close_repo: bool
        """
        self.repository = repository
        self.mini_repository = repository.mini_repository
        self.is_closed = False
        self._add_commit_size = None
        self._close_repo = close_repo
        self.is_session_active = False 
[docs]    def getSpec(self):
        """
        Get the session specification string for this repository.
        :return: Spec string suitable for use with
                 :meth:`~franz.openrdf.sail.allegrographserver.AllegroGraphServer.openSession`.
        """
        return self.repository.getSpec() 
    def _get_mini_repository(self):
        return self.mini_repository
[docs]    def getValueFactory(self):
        """
        Get the :class:`.ValueFactory` associated with this repository.
        Note that it is recommended to use methods defined in :class:`RepositoryConnection`
        instead of a value factory. The latter is only provided for RDF4J compatibility.
        :return: A value factory.
        :rtype: ValueFactory
        """
        return self.repository.getValueFactory() 
[docs]    def close(self):
        """
        Close the connection. This also closes the session if it is active.
        It is safe to call this on a connection that has already been closed.
        Note that using ``with`` is the preferred way to manage connections.
        """
        if not self.is_closed:
            self.closeSession()
            self.is_closed = True
            if self._close_repo:
                self.repository.shutDown() 
[docs]    def setAddCommitSize(self, triple_count):
        """
        Set the value of :attr:`.add_commit_size`.
        :param triple_count: Value of :attr:`.add_commit_size`.
        """
        if not triple_count or triple_count < 0:
            self._add_commit_size = None
        else:
            self._add_commit_size = int(triple_count) 
[docs]    def getAddCommitSize(self):
        """
        Get the current value of :attr:`.add_commit_size`.
        :return: Value of :attr:`.add_commit_size`.
        """
        return self._add_commit_size 
    add_commit_size = property(
        getAddCommitSize, setAddCommitSize,
        doc="""The threshold for commit size during triple add operations.
               Set to 0 (zero) or None to clear size-based autocommit behavior.
               When set to an integer triple_count > 0, a commit will occur every
               triple_count triples added and at the end of the triples being added.""")
[docs]    def prepareQuery(self, queryLanguage, queryString, baseURI=None):
        """
        Embed 'queryString' into a query object which can be
        executed against the RDF storage.
        """
        query = Query(queryLanguage, queryString, baseURI)
        query.setConnection(self)
        return query 
[docs]    def prepareTupleQuery(self, queryLanguage=QueryLanguage.SPARQL,
                          query=None, baseURI=None, queryString=None):
        """
        Parse ``query`` into a tuple query object which can be
        executed against the triple stroe.
        :param queryLanguage: Either ``QueryLanguage.SPARQL`` or ``QueryLanguage.PROLOG``.
        :type queryLanguage: QueryLanguage
        :param query: The query string (must contain a ``SELECT`` query).
        :type query: string
        :param baseURI: An optional base used to resolve relative URIs in the query.
        :type baseURI: string|URI
        :param queryString: Legacy name of the ``query`` parameter.
        :type queryString: string
        :return: A query object.
        :rtype: TupleQuery
        """
        query = TupleQuery(queryLanguage, query or queryString, baseURI=baseURI)
        query.setConnection(self)
        return query 
[docs]    def prepareUpdate(self, queryLanguage=QueryLanguage.SPARQL,
                      query=None, baseURI=None, queryString=None):
        """
        Parse ``query`` into an update query object which can be
        executed against the triple store.
        :param queryLanguage: Either ``QueryLanguage.SPARQL`` or ``QueryLanguage.PROLOG``.
        :type queryLanguage: QueryLanguage
        :param query: The query string (must contain an ``UPDATE`` query).
        :type query: string
        :param baseURI: An optional base used to resolve relative URIs in the query.
        :type baseURI: string|URI
        :param queryString: Legacy name of the ``query`` parameter.
        :type queryString: string
        :return: A query object.
        :rtype: UpdateQuery
        """
        query = UpdateQuery(queryLanguage, query or queryString,
                            baseURI=baseURI)
        query.setConnection(self)
        return query 
[docs]    def prepareGraphQuery(self, queryLanguage=QueryLanguage.SPARQL,
                          query=None, baseURI=None, queryString=None):
        """
        Parse ``query`` into a graph query object which can be
        executed against the triple store.
        :param queryLanguage: Either ``QueryLanguage.SPARQL`` or ``QueryLanguage.PROLOG``.
        :type queryLanguage: QueryLanguage
        :param query: The query string (must be a``CONSTRUCT`` or ``DESCRIBE`` query).
        :type query: string
        :param baseURI: An optional base used to resolve relative URIs in the query.
        :type baseURI: string|URI
        :param queryString: Legacy name of the ``query`` parameter.
        :type queryString: string
        :return: A query object.
        :rtype: GraphQuery
        """
        query = GraphQuery(queryLanguage, query or queryString, baseURI=baseURI)
        query.setConnection(self)
        return query 
[docs]    def prepareBooleanQuery(self, queryLanguage=QueryLanguage.SPARQL,
                            query=None, baseURI=None, queryString=None):
        """
        Parse ``query`` into a boolean query object which can be
        executed against the triple store.
        :param queryLanguage: Either ``QueryLanguage.SPARQL`` or ``QueryLanguage.PROLOG``.
        :type queryLanguage: QueryLanguage
        :param query: The query string (must contain an ``ASK`` query).
        :type query: string
        :param baseURI: An optional base used to resolve relative URIs in the query.
        :type baseURI: string|URI
        :param queryString: Legacy name of the ``query`` parameter.
        :type queryString: string
        :return: A query object.
        :rtype: BooleanQuery
        """
        query = BooleanQuery(queryLanguage, query or queryString,
                             baseURI=baseURI)
        query.setConnection(self)
        return query 
[docs]    def getContextIDs(self):
        """
        Return a list of context resources, one for each context referenced by a quad in
        the triple store. Note that the default context will not be included in the result.
        :return: A list of contexts (as :class:`URI` objects).
        :rtype: list[URI]
        """
        contexts = []
        for cxt in self._get_mini_repository().listContexts():
            contexts.append(self.createURI(cxt))
        return contexts 
[docs]    def size(self, contexts=ALL_CONTEXTS):
        """
        Returns the number of (explicit) statements that are in the specified
        contexts in this repository.
        :param contexts: List of contexts (graph URIs) to count the statements in.
                         By default statements in all graphs will be counted.
        :type contexts: Iterable[string|URI]
        """
        cxts = self._contexts_to_ntriple_contexts(contexts, False)
        if cxts == ALL_CONTEXTS or not cxts:
            return self._get_mini_repository().getSize()
        elif len(cxts) == 1:
            return self._get_mini_repository().getSize(cxts[0])
        else:
            total = 0
            for cxt in cxts:
                total += self._get_mini_repository().getSize(cxt)
            return total 
[docs]    def isEmpty(self):
        """
        Return ``True`` if this repository does not contain any (explicit) statements.
        """
        return self.size() == 0 
    def _context_to_ntriples(self, context, none_is_mini_null=False):
        if context is None:
            return MINI_NULL_CONTEXT if none_is_mini_null else None
        if context == MINI_NULL_CONTEXT:
            return MINI_NULL_CONTEXT
        if context == 'null':
            return MINI_NULL_CONTEXT
        if context:
            return context if isinstance(context, basestring) else context.toNTriples()
        if none_is_mini_null:
            return MINI_NULL_CONTEXT
        return None
    def _contexts_to_ntriple_contexts(self, contexts, none_is_mini_null=False):
        """
        Do three transformations here.  Convert from context object(s) to
        context strings (angle brackets).
        Also, convert singleton context to list of contexts, and convert
        ALL_CONTEXTS to None.
        And, convert None context to 'null'.
        """
        if contexts == ALL_CONTEXTS:
            cxts = None
        elif contexts is None:
            if none_is_mini_null: cxts = [MINI_NULL_CONTEXT]
            else: cxts = None
        elif contexts == 'null':
            cxts = [MINI_NULL_CONTEXT]
        elif isinstance(contexts, (list, tuple)):
            cxts = [self._context_to_ntriples(c, none_is_mini_null=True) for c in contexts]
        else:
            cxts = [self._context_to_ntriples(contexts, none_is_mini_null=True)]
        return cxts
    def _convert_term_to_mini_term(self, term, predicate_for_object=None):
        """
        If 'term' is a Value, convert it to an ntriples string.  If its a Python
        term, do likewise
        If 'term' is a CompoundLiteral or a list or tuple, separate out the second
        value, ntriplize it, and return a binary tuple.
        TODO: FIGURE OUT HOW COORDINATE PAIRS WILL WORK HERE
        """
        if isinstance(term, GeoSpatialRegion): return term
        factory = self.getValueFactory()
        if isinstance(term, GeoCoordinate):
            geoType = term.geoType
            miniGeoType = geoType._getMiniGeoType()
            if geoType.system == GeoType.Cartesian:
                return self._get_mini_repository().createCartesianGeoLiteral(miniGeoType, term.xcoor, term.ycoor)
            elif geoType.system == GeoType.Spherical:
                unit = term.unit or term.geoType.unit
                return self._get_mini_repository().createSphericalGeoLiteral(miniGeoType, term.xcoor, term.ycoor, unit=unit)
            else:
                raise IllegalOptionException("Unsupported geo coordinate system", geoType.system)
        if isinstance(term, RangeLiteral):
            beginTerm = term.getLowerBound()
            endTerm = term.getUpperBound()
            return (self._to_ntriples(beginTerm), self._to_ntriples(endTerm))
        elif isinstance(term, (tuple, list)):
            return [self._convert_term_to_mini_term(t, predicate_for_object=predicate_for_object) for t in term]
        ## OBSOLETE: CONVERT LIST TO RANGE LITERAL:
        elif isinstance(term, (tuple, list)):
            factory = self.getValueFactory()
            beginTerm = factory.object_position_term_to_openrdf_term(term[0])
            factory.validateRangeConstant(beginTerm, predicate_for_object)
            endTerm = factory.object_position_term_to_openrdf_term(term[1])
            factory.validateRangeConstant(endTerm, predicate_for_object)
            return (self._to_ntriples(beginTerm), self._to_ntriples(endTerm))
        ## END OBSOLETE
        elif predicate_for_object:
            term = factory.object_position_term_to_openrdf_term(term, predicate=predicate_for_object)
            return self._to_ntriples(term)
        else:
            return self._to_ntriples(term)
[docs]    def getStatements(self, subject=None, predicate=None,  object=None, contexts=ALL_CONTEXTS, includeInferred=False,
                       limit=None, offset=None, tripleIDs=False, output=None, output_format=RDFFormat.NQX):
        """
        Get all statements with a specific subject, predicate and/or
        object from the repository. The result is optionally
        restricted to the specified set of named contexts (graphs).
        Return a :class:`RepositoryResult` object that can be used to
        iterate over the resulting statements and filter out
        duplicates if desired. Alternatively one can write the 
        results to a file or stream using the ``output`` parameter.
        :param subject: Subject value or ``None`` (no subject filtering).
        :type subject: Value
        :param predicate: Predicate value or ``None`` (no predicatefiltering).
        :type predicate: URI
        :param object: Object value or ``None`` (no object filtering).
        :type object: Value
        :param contexts: An optional list of graphs to retrieve the 
                         statements from.
                         By default statements are taken from all graphs.
        :type contexts: URI|string|Iterable[URI|string]
        :param includeInferred: If ``True``, include triples inferred through 
                                RDFS++ reasoning.
                                The default is ``False``.
        :type includeInferred: bool
        :param limit: Max number of statements to retrieve (optional).
        :type limit: int
        :param offset: Used in conjunction with ``limit`` to return results 
                       starting from the nth statement.
        :type offset: int
        :param tripleIDs: If ``True`` the id field will be filled 
                          in the returned statements.
        :param output: File path or a file-like object to write
                       the result to.
        :type output: str|file
        :param output_format: Serialization format for ``output``.
        :type output_format: RDFFormat
        :return: An iterator over the resulting statements 
                 or ``None`` (if ``output`` is used).
        :rtype: RepositoryResult
        """
        # note: so with tripleIDs we'll get triples that are quads consisting of five elements.
        with output_to(output) as out_file:
            callback = None if output is None else out_file.write
            accept = None if output is None else RDFFormat.mime_type_for_format(output_format)
            subj = self._convert_term_to_mini_term(subject)
            pred = self._convert_term_to_mini_term(predicate)
            obj = self._convert_term_to_mini_term(object, predicate)
            cxt = self._contexts_to_ntriple_contexts(contexts)
            if isinstance(object, GeoSpatialRegion):
                if cxt is not None and cxt != ALL_CONTEXTS:
                    raise ValueError('Geospatial queries cannot be limited to a context.')
                return self._getStatementsInRegion(subj, pred, obj, limit=limit, offset=offset,
                                                   accept=accept, callback=callback)
            else:
                result = self._get_mini_repository().getStatements(
                    subj, pred, obj, cxt,
                    infer=includeInferred, limit=limit, offset=offset, tripleIDs=tripleIDs,
                    accept=accept, callback=callback)
            if output is None:
                return RepositoryResult(result, tripleIDs=tripleIDs)
            else:
                return None 
[docs]    def getStatementsById(self, ids, output=None, output_format=RDFFormat.NQX):
        """
        Return all statements whose triple ID matches an ID in the list 'ids'.
        :param ids: List of statement ids.
        :type ids: Iterable[int]
        :param output: File path or a file-like object to write
                       the result to.
        :type output: str|file
        :param output_format: Serialization format for ``output``.
        :type output_format: RDFFormat
        :return: An iterator over requested statements
                 or ``None`` (if ``output`` is used).
        :rtype: RepositoryResult
        """
        with output_to(output) as out_file:
            callback = None if output is None else out_file.write
            accept = None if output is None else RDFFormat.mime_type_for_format(output_format)
            result = self._get_mini_repository().getStatementsById(ids, accept=accept, callback=callback)
        if output is None:
            return RepositoryResult(result, tripleIDs=False)
        else:
            return False 
    def _getStatementsInRegion(self, subject, predicate, region, limit=None, offset=None, accept=None, callback=None):
        geoType = region.geoType
        miniGeoType = geoType._getMiniGeoType()
        common_args = {
            'limit': limit,
            'offset': offset,
            'accept': accept,
            'callback': callback
        }
        if isinstance(region, GeoBox):
            if geoType.system == GeoType.Cartesian:
                stringTuples = self._get_mini_repository().getStatementsInsideBox(
                    miniGeoType, predicate, region.xMin, region.xMax, region.yMin, region.yMax,
                    **common_args)
            elif geoType.system == GeoType.Spherical:
                stringTuples = self._get_mini_repository().getStatementsInsideBox(
                    miniGeoType, predicate, region.yMin, region.yMax, region.xMin, region.xMax,
                    **common_args)
            else:
                raise ValueError('Unsupported coordinate system: %s' % geoType.system)
        elif isinstance(region, GeoCircle):
            if geoType.system == GeoType.Cartesian:
                stringTuples = self._get_mini_repository().getStatementsInsideCircle(miniGeoType, predicate,
                                        region.x, region.y, region.radius, **common_args)
            elif geoType.system == GeoType.Spherical:
                stringTuples = self._get_mini_repository().getStatementsHaversine(miniGeoType, predicate,
                                        region.x, region.y, region.radius, unit=region.unit,
                                        **common_args)
            else:
                raise ValueError('Unsupported coordinate system: %s' % geoType.system)
        elif isinstance(region, GeoPolygon):
            stringTuples = self._get_mini_repository().getStatementsInsidePolygon(miniGeoType, predicate,
                                        self._convert_term_to_mini_term(region.getResource()),
                                        **common_args)
        else:
            raise ValueError('Unsupported region type: %s' % type(region))     # can't happen
        if callback is None:
            return RepositoryResult(stringTuples, subjectFilter=subject)
        else:
            return None
    # NOTE: 'format' shadows a built-in symbol but it is too late to change the public API
[docs]    def add(self, arg0, arg1=None, arg2=None, contexts=None, base=None, format=None, serverSide=False):
        """
        Calls :meth:`.addTriple`, :meth:`.addStatement`, or :meth:`.addFile`.
        Best practice is to avoid ``add()`` and use :meth:`addTriple`, :meth:`addStatement`
        or :meth:`addFile` instead.
        :param arg0: May be a Statement or a filepath. If so, arg1 and arg2 default to None.
                     May also be the subject of a triple (in that case arg1 and arg2 must be
                     the predicate and the object respectively).
        :type arg0: Statement|string|Value
        :param arg1: Predicate of the new triple.
        :type arg1: string|Value
        :param arg2: Object of the new triple.
        :type arg2: string|Value
        :param contexts: Optional list of contexts (subgraph URIs), defaulting to None.
                         A context is the URI of a subgraph. If None, the triple(s) will
                         be added to the null context (the default or background graph).
        :type contexts: Iterable[URI|string]
        :param base: The baseURI to associate with loading a file.  Defaults to ``None``.
                     If ``None`` the baseURI will be chosen by the server.
        :type base: string
        :param format: Either ``RDFFormat.NTRIPLES`` or ``RDFFormat.RDFXML``. Defaults to ``None``.
        :type format: RDFFormat
        :param serverSide: Indicates whether the filepath refers to a file on the client computer
                           or on the server.  Defaults to ``False`` (i.e. client-side).
        :type serverSide: bool
        """
        if contexts and not isinstance(contexts, list):
            contexts = [contexts]
        if isinstance(arg0, (basestring, file)):
            if contexts:
                if len(contexts) > 1:
                    raise IllegalArgumentException("Only one context may be specified when loading from a file.")
                context = contexts[0]
            else:
                context = None
            return self.addFile(arg0, base=base, format=format, context=context, serverSide=serverSide)
        elif isinstance(arg0, Value):
            return self.addTriple(arg0, arg1, arg2, contexts=contexts)
        elif isinstance(arg0, Statement):
            return self.addStatement(arg0, contexts=contexts)
        elif hasattr(arg0, '__iter__'):
            for s in arg0:
                self.addStatement(s, contexts=contexts)
        else:
            raise IllegalArgumentException("Illegal first argument to 'add'.  Expected a Value, Statement, File, or string.") 
    # NOTE: 'format' shadows a built-in symbol but it is too late to change the public API
[docs]    def addFile(self, filePath, base=None, format=None, context=None, serverSide=False, content_encoding=None):
        """
        Loads a file into the triple store. Note that a file can be loaded into only one context.
        :param filepath: Identifies the file to load.
        :type filePath: string
        :param context: An optional context URI (subgraph URI), defaulting to ``None``.
                        If ``None``, the triple(s) will be added to the null context
                        (the default or background graph).
        :type context: URI|string|list[URI|string]
        :param base: The baseURI to associate with loading a file.  Defaults to ``None``.
                     If ``None`` the baseURI will be chosen by the server.
        :type base: string
        :param format: Either ``RDFFormat.NTRIPLES`` or ``RDFFormat.RDFXML``. Defaults to ``None``.
        :type format: RDFFormat
        :param serverSide: Indicates whether the filepath refers to a file on the client computer
                           or on the server.  Defaults to ``False`` (i.e. client-side).
        :type serverSide: bool
        """
        if isinstance(context, (list, tuple)):
            if len(context) > 1:
                raise IllegalArgumentException("Multiple contexts passed to 'addFile': %s" % context)
            context = context[0] if context else None
        contextString = self._context_to_ntriples(context, none_is_mini_null=True)
        fmt, ce = RDFFormat.format_for_file_name(filePath)
        format = format or fmt
        content_encoding = content_encoding or ce
    
        self._get_mini_repository().loadFile(
            filePath, format, context=contextString, serverSide=serverSide,
            commitEvery=self.add_commit_size, baseURI=base,
            content_encoding=content_encoding) 
[docs]    def addData(self, data, rdf_format=RDFFormat.TURTLE, base_uri=None, context=None):
        """
        Adds data from a string to the repository.
        :param data: Data to be added.
        :type data: string
        :param rdf_format: Data format - either a RDFFormat or a MIME type (string).
        :type rdf_format: RDFFormat|str
        :param base_uri: Base for resolving relative URIs.
                         If None (default), the URI will be chosen by the server.
        :type base_uri: string
        :param context: Graph to add the data to.
                        If None (default) the default graph will be used.
        :type context: URI|string
        """
        ctx = self._context_to_ntriples(context, none_is_mini_null=True)
        self._get_mini_repository().loadData(
            data, rdf_format, base_uri=base_uri, context=ctx) 
[docs]    def addTriple(self, subject, predicate, object, contexts=None):
        """
        Add a single triple to the repository.
        :param subject: Subject of the new triple.
        :type subject: Value
        :param predicate: Predicate of the new triple.
        :type predicate: Value
        :param object: Object of the new triple.
        :type object: Value
        :param contexts: List of contexts (graph URIs) to add the triple to.
                         Defaults to ``None``, which adds the statement to the
                         null context (the default or background graph).
        :type contexts: Iterable[string|URI]
        """
        obj = self.getValueFactory().object_position_term_to_openrdf_term(object, predicate=predicate)
        cxts = self._contexts_to_ntriple_contexts(contexts, none_is_mini_null=True)
        for cxt in cxts:
            self._get_mini_repository().addStatement(self._to_ntriples(subject), self._to_ntriples(predicate),
                        self._convert_term_to_mini_term(obj), cxt) 
    def _to_ntriples(self, term):
        """
        If 'term' is an OpenRDF term, convert it to a string.  If it's already
        a string, assume it's in ntriples format, and just pass it through.
        If the term is None, return None.
        Otherwise convert `term` to Literal and make a string from that.
        """
        if term is None or isinstance(term, basestring):
            return term
        elif hasattr(term, 'toNTriples'):
            return term.toNTriples()
        else:
            return Literal(term).toNTriples()
[docs]    def addTriples(self, triples_or_quads, context=None, ntriples=False):
        """
        Add the supplied triples or quads to this repository.
        :param triples_or_quads: List of triples or quads. Each element can be
                                 either a statement or a list or tuple of :class:`Value` objects
                                 or strings.
        :type triples_or_quads: Iterable[list[string|Value]|tuple[string|Value]|Statement]
        :param context: Context (graph) or list of contexts to add the triples to.
                        Defaults to None (the default graph). Note that this will
                        be ignored for all input quads that already specify a context.
        :type context: string|URI|list[string|URI]
        :param ntriples: If ``True``, parts of the triples are assumed to be strings
                         in N-Triples format and are sent to the server without any
                         conversion.
        :type ntriples: bool
        """
        quads = []
        for q in triples_or_quads:
            # Note: Statement objects will work here, since they have a length
            # and support accessing components by index.
            is_quad = len(q) == 4
            if ntriples:
                s = q[0]
                p = q[1]
                o = q[2]
                gs = q[3] if is_quad and q[3] else context
            else:
                predicate = q[1]
                obj = self.getValueFactory().object_position_term_to_openrdf_term(q[2], predicate=predicate)
                s = self._to_ntriples(q[0])
                p = self._to_ntriples(predicate)
                o = self._to_ntriples(obj)
                gs = [self._to_ntriples(q[3])] if is_quad and q[3] else context
            if gs is None:
                quads.append((s, p, o, None))
            else:
                for g in self._contexts_to_ntriple_contexts(gs, none_is_mini_null=True):
                    quads.append((s, p, o, g))
        self._get_mini_repository().addStatements(quads, commitEvery=self.add_commit_size) 
[docs]    def addStatement(self, statement, contexts=None):
        """
        Add the supplied statement to the specified contexts in the repository.
        :param statement: The statement to be added. Note that the ``context`` field is
                          ignored (use the ``contexts`` parameters instead).
        :type statement: Statement
        :param contexts: List of contexts (graph URIs) to add the statement to.
                         Defaults to ``None``, which adds the statement to the
                         null context (the default or background graph).
        :type contexts: Iterable[string|URI]
        """
        self.addTriple(statement.getSubject(), statement.getPredicate(), statement.getObject(),
                       contexts=contexts) 
[docs]    def remove(self, arg0, arg1=None, arg2=None, contexts=None):
        """
        Call :meth:`removeTriples` or :meth:`removeStatement`.
        Best practice is to avoid :meth:`remove` and use :meth:`removeTriples`
        or :meth:`removeStatement` directly.
        Note that ``context`` fields of statements passed to this method are ignored.
        ``arg0`` may be a :class:`Statement`.  If so, then ``arg1`` and ``arg2`` default to None.
        ``arg0``, ``arg1``, and ``arg2`` may be the subject, predicate and object of a triple.
        :param arg0: Either a :class:`Statement`, a list of statements or the subject of a triple.
        :type arg0: Statement|Value|Iterable[Statement]
        :param arg1: Predicate of a triple.
        :type arg1: URI
        :param arg2: Object of a triple.
        :type arg2: Value
        :param contexts: An optional list of graphs to remove triples from.
        :type contexts: Iterable[URI|string]
        """
        if contexts and not isinstance(contexts, list):
            contexts = [contexts]
        if isinstance(arg0, Value) or arg0 is None: self.removeTriples(arg0, arg1, arg2, contexts=contexts)
        elif isinstance(arg0, Statement): self.removeStatement(arg0, contexts=contexts)
        elif hasattr(arg0, '__iter__'):
            for s in arg0:
                self.removeStatement(s, contexts=contexts)
        else:
            raise IllegalArgumentException("Illegal first argument to 'remove'.  Expected a Value, Statement, or iterator.") 
[docs]    def removeTriples(self, subject, predicate, object, contexts=ALL_CONTEXTS):
        """
        Remove the statement(s) with the specified subject, predicate and object
        from the repository, optionally restricted to the specified contexts.
        :param subject: Subject of the triples to be removed.
        :type subject: Value
        :param predicate: Predicate of the triples to be removed.
        :type predicate: URI
        :param object: Object of the triples to be removed.
        :type object: Value
        :param contexts: An optional list of graphs to remove triples from.
        :type contexts: Iterable[URI|string]
        """
        subj = self._to_ntriples(subject)
        pred = self._to_ntriples(predicate)
        obj = self._to_ntriples(self.getValueFactory().object_position_term_to_openrdf_term(object))
        ntripleContexts = self._contexts_to_ntriple_contexts(contexts, none_is_mini_null=True)
        if ntripleContexts is None or len(ntripleContexts) == 0:
            self._get_mini_repository().deleteMatchingStatements(subj, pred, obj, None)
        else:
            for cxt in ntripleContexts:
                self._get_mini_repository().deleteMatchingStatements(subj, pred, obj, cxt) 
[docs]    def removeQuads(self, quads, ntriples=False):
        """
        Remove enumerated quads from this repository.  Each quad can
        be a list or a tuple of :class:`Value` objects.
        :param quads: List of quads. Each element can be
                      either a statement or a list or tuple of :class:`Value` objects
                      or strings.
        :type quads: Iterable[list[string|Value]|tuple[string|Value]]
        :param ntriples: If ``True``, parts of the quads are assumed to be strings
                         in N-Triples format and are sent to the server without any
                         conversion.
        :type ntriples: bool
        """
        removeQuads = []
        for q in quads:
            quad = [None] * 4
            if ntriples:
                quad[0] = q[0]
                quad[1] = q[1]
                quad[2] = q[2]
                quad[3] = q[3]
            elif isinstance(quad, (list, tuple)):
                predicate = q[1]
                obj = self.getValueFactory().object_position_term_to_openrdf_term(q[2], predicate=predicate)
                quad[0] = self._to_ntriples(q[0])
                quad[1] = self._to_ntriples(predicate)
                quad[2] = self._to_ntriples(obj)
                quad[3] = self._to_ntriples(q[3])
            else: # must be a statement
                predicate = q.getPredicate()
                obj = self.getValueFactory().object_position_term_to_openrdf_term(q.getObject(), predicate=predicate)
                quad[0] = self._to_ntriples(q.getSubject())
                quad[1] = self._to_ntriples(predicate)
                quad[2] = self._to_ntriples(obj)
                quad[3] = self._to_ntriples(q.getContext())
            removeQuads.append(quad)
        self._get_mini_repository().deleteStatements(removeQuads) 
[docs]    def removeQuadsByID(self, tids):
        """
        Remove all quads with matching IDs.
        :param tids: List of IDs to be removed.
        :type tids: list[int]
        """
        self._get_mini_repository().deleteStatementsById(tids) 
[docs]    def removeStatement(self, statement, contexts=None):
        """
        Remove the supplied statement from the specified contexts in the repository.
        :param statement: Statement to be removed. Note that the ``context`` field of this object
                          will be ignored.
        :type statement: Statement
        :param contexts: An optional list of graphs to remove the statement from.
        :type contexts: Iterable[URI|string]
        """
        self.removeTriples(statement.getSubject(), statement.getPredicate(), statement.getObject(), contexts=contexts) 
[docs]    def clear(self, contexts=ALL_CONTEXTS):
        """
        Removes all statements from designated contexts in the repository.  If
        ``contexts`` is ALL_CONTEXTS, clears the repository of all statements.
        :param contexts: A context or list of contexts.
        :type contexts: Iterable[string|URI]|string|URI
        """
        self.removeTriples(None, None, None, contexts=contexts) 
[docs]    def export(self, handler, contexts=ALL_CONTEXTS):
        """
        Export all explicit statements in the specified contexts to a file.
        .. deprecated:: 4.14.1
           Use :meth:`saveResponse` instead.
        
        :param handler: An RDFWriter instance describing the target file and data format.
        :type handler: RDFWriter
        :param contexts: A context or list of contexts (default: all contexts).
        :type contexts: Iterable[string|URI]|string|URI
        """
        warnings.warn("export is deprecated. Use saveResponse instead.", DeprecationWarning, stacklevel=2)
        self.exportStatements(None, None, None, False, handler, contexts=contexts) 
[docs]    def exportStatements(self, subj, pred, obj, includeInferred, handler, contexts=ALL_CONTEXTS):
        """
        Export statements to a file.
        .. deprecated:: 4.14.1
           Use :meth:`saveResponse` instead.
        :param subj: Subject or ``None`` (i.e. no filtering on subject).
        :type subj: Value
        :param pred: Predicate or ``None`` (i.e. no filtering on predicate).
        :type pred: URI
        :param obj: Object or ``None`` (i.e. no filtering on object).
        :type obj: Value
        :param includeInferred: If ``True`` inferred triples will be included
                                in the output. The default is ``False``.
        :type includeInferred: bool
        :param handler: An RDFWriter instance describing the target file and data format.
        :type handler: RDFWriter
        :param contexts: A context or list of contexts (default: all contexts).
        :type contexts: Iterable[string|URI]|string|URI
        """
        warnings.warn("exportStatements is deprecated. Use getStatements(output=...) instead.",
                      DeprecationWarning, stacklevel=2)
        self.getStatements(subj, pred, obj, contexts,
                           output=handler.getFilePath() or sys.stdout,
                           output_format=handler.getRDFFormat(),
                           includeInferred=includeInferred) 
[docs]    def getSubjectTriplesCacheSize(self):
        """
        Return the current size of the subject triples cache.
        .. seealso::
           :meth:`.enablerSubjectTriplesCache`
        """
        return self._get_mini_repository().getTripleCacheSize() 
[docs]    def disableSubjectTriplesCache(self):
        """
        Disable the subject triples cache (see :meth:`.enableSubjectTriplesCache`).
        """
        self._get_mini_repository().disableTripleCache() 
[docs]    def enableSubjectTriplesCache(self, size=None):
        """
        Maintain a cache of size ``size`` that caches, for each accessed
        resource, quads where the resource appears in subject position.
        This can accelerate the performance of certain types of queries.
        :param size: The maximum number of subjects whose triples will be cached.
                     Default is 100,000.
        :type size: int
        """
        self._get_mini_repository().enableTripleCache(size=size) 
    ## Indexing control methods
[docs]    def listIndices(self):
        """
        Return the list of the current set of triple indices.
        Index names are strings such as ``"gospi"``, ``"spogi"`` etc.
        Use :meth:`.listValidIndices` to get the list of index names
        supported by the server.
        :return: List of index names (see :meth:`.listValidIndices`).
        :rtype: list[string]
        """
        return self._get_mini_repository().listIndices() 
[docs]    def listValidIndices(self):
        """
        Return the list of valid index names.
        :return: List of index names.
        :rtype: list[string]
        """
        return self._get_mini_repository().listValidIndices() 
[docs]    def addIndex(self, _type):
        """
        Add a specific type of index to the current set of triple indices.
        :param _type: Index name (see :meth:`.listIndices`).
        :type _type: string
        """
        return self._get_mini_repository().addIndex(_type) 
[docs]    def dropIndex(self, _type):
        """
        Removes a specific type of index to the current set of triple indices.
        :param _type: Index name (see :meth:`.listIndices`).
        :type _type: string
        """
        return self._get_mini_repository().dropIndex(_type) 
[docs]    def optimizeIndices(self, level=None, wait=None):
        """
        Optimize indices.
        Please see documentation for argument values and meanings:
        https://franz.com/agraph/support/documentation/current/triple-index.html#optimize
        """
        return self._get_mini_repository().optimizeIndices(level, wait) 
    #############################################################################################
    ## ValueFactory methods
    ## Added here because its inconvenient to have to dispatch from three different objects
    ## (connection, repository, and value factory) when one will do
    #############################################################################################
[docs]    def registerDatatypeMapping(self, predicate=None, datatype=None, nativeType=None):
        """
        Register an inlined datatype.
        See :meth:`.Repository.registerDatatypeMapping`.
        """
        return self.repository.registerDatatypeMapping(predicate=predicate, datatype=datatype, nativeType=nativeType) 
[docs]    def createLiteral(self, value, datatype=None, language=None):
        """
        Create a new literal with value ``value``.  ``datatype``, if supplied,
        should be a URI (it can be a string or an :class:`.URI` instance, in which
        case ``value`` must be a string.
        :param value: Literal value - can be a string, a number or a datetime object.
        :type value: int|long|float|string|datetime.datetime|datetime.date|datetime.time
        :param datatype: Optional literal type. Note that if ``value`` is not a string
                         the type will be guessed automatically.
        :type datatype: string|URI
        :param language: Optional language tag.
        :type language: string
        """
        return self.getValueFactory().createLiteral(value, datatype=datatype, language=language) 
[docs]    def createURI(self, uri=None, namespace=None, localname=None):
        """
        Creates a new URI from the supplied string-representation(s).
        If two non-keyword arguments are passed, assumes they represent a
        namespace/localname pair.
        :param uri: URI text, unless namespace is passed and localname is not,
                    in which case this becomes the namespace.
        :type uri: string
        :param namespace: Namespace part of the URI if ``localname`` is passed,
                          otherwise this becomes the local name.
        :type namespace: string|URI
        :param localname: Local part of the URI. Should only be used as a
                          keyword argument and together with ``namespace``.
        :return: An URI object.
        :rtype: URI
        """
        return self.getValueFactory().createURI(uri=uri, namespace=namespace, localname=localname) 
[docs]    def createBNode(self, nodeID=None):
        """
        Create a new blank node.
        :param nodeID: Optional node identifier, if not given a fresh one will be generated.
        :type nodeID: string
        :return: A BNode value.
        :rtype: BNode
        """
        return self.getValueFactory().createBNode(nodeID=nodeID) 
[docs]    def createStatement(self, subject, predicate, object, context=None):
        """
        Create a new Statement object.
        Note that this does *not* cause the statement to be added to the repository.
        :param subject: Subject of the new statement.
        :type subject: Resource
        :param predicate: Predicate of the new statement.
        :type predicate: URI
        :param object: Object of the new statement.
        :type object: Value
        :param context: Graph of the new statement (optional).
        :type context: URI
        :return: A new statement.
        :rtype: Statement
        """
        return self.getValueFactory().createStatement(subject, predicate, object, context=context) 
[docs]    def createRange(self, lowerBound, upperBound):
        """
        Create a compound literal representing a range from lowerBound to upperBound.
        :param lowerBound: Lower bound of the range.
        :type lowerBound: int
        :param upperBound: Upper bound of the range.
        :type upperBound: int
        :return: A new literal object.
        :rtype: RangeLiteral
        """
        return self.getValueFactory().createRange(lowerBound=lowerBound, upperBound=upperBound) 
[docs]    def addRules(self, rules, language=QueryLanguage.PROLOG):
        """
        Add Prolog functors to the current session.
        Note that this only works with a dedicated session (See :meth:`.session`.
        .. seealso::
           http://franz.com/agraph/support/documentation/current/prolog-tutorial.html
              Tutorial describing the syntax of Prolog rules (functors).
        :param rules: A string containing Prolog rule definitions. The definitions
                      are S-expressions using the ``<-`` and ``<--`` operators
                      (to append and overwrite rules respectively).
        :type rules: string
        :param language: Ignored.
        """
        del language
        if not self._get_mini_repository().sessionAlive:
            raise Exception('Prolog rules can only be added in a dedicated session.')
        self._get_mini_repository().definePrologFunctors(rules) 
[docs]    def loadRules(self, filename, language=QueryLanguage.PROLOG):
        """
        Add Prolog rules from file to the current session.
        Note that this only works with a dedicated session.
        :param filename: Path to the file containing Prolog rules.
        :type filename: string
        :param language: Ignored.
        """
        with open(filename) as _file:
            body = _file.read()
        self.addRules(body, language) 
    #############################################################################################
    ## Server-side implementation of namespaces
    #############################################################################################
[docs]    def getNamespaces(self):
        """
        Get all declared prefix/namespace pairs.
        The result is a dictionary mapping prefixes to namespace URIs.
        :return: A dictionary of namespaces.
        :rtype: dict[string,string]
        """
        namespaces = {}
        for pair in self._get_mini_repository().listNamespaces():
            namespaces[pair['prefix']] = pair['namespace']
        return namespaces 
[docs]    def getNamespace(self, prefix):
        """
        Get the namespace that is associated with the specified prefix, if any.
        :param prefix: Namespace prefix.
        :return: Namespace URI.
        :raises RequestError: if there is no namespace with given prefix.
        """
        return self._get_mini_repository().getNamespace(prefix) 
[docs]    def setNamespace(self, prefix, name):
        """
        Define or redefine a namespace mapping in the repository.
        :param prefix: Namespace prefix.
        :type prefix: string
        :param name: Namespace URI.
        :type name: string
        """
        self._get_mini_repository().addNamespace(prefix, name) 
[docs]    def removeNamespace(self, prefix):
        """
        Remove a namespace declaration by removing the association between a
        prefix and a namespace name.
        :param prefix: Namespace prefix.
        :type prefix: string
        """
        self._get_mini_repository().deleteNamespace(prefix) 
[docs]    def clearNamespaces(self, reset=True):
        """
        Delete all namespaces in this repository for the current user.
        :param reset: If ``True`` (default) the user's namespaces are reset
                      to the default set of namespaces, otherwise all namespaces
                      are cleared.
        """
        self._get_mini_repository().clearNamespaces(reset) 
    #############################################################################################
    ## Geo-spatial
    #############################################################################################
[docs]    def createRectangularSystem(self, scale=1, unit=None, xMin=0, xMax=None, yMin=0, yMax=None):
        """
        Create a Cartesian coordinate system and use it as the current coordinate system.
        :param scale: Estimate of the Y size of a typical search region.
        :type scale: float|int
        :param unit: Must be ``None`` (the default).
        :param xMin: Left edge of the rectangle.
        :type xMin: float|int
        :param xMax: Right edge of the rectangle.
        :type xMax: float|int
        :param yMin: Bottom edge of the rectangle.
        :type yMin: float|int
        :param yMax: Top edge of the rectangle.
        :type yMax: float|int
        :return: The new coordinate system.
        """
        self.geoType = GeoType(GeoType.Cartesian, scale=scale, unit=unit, xMin=xMin, xMax=xMax, yMin=yMin, yMax=yMax)
                               ##,latMin=None, latMax=None, longMin=None, longMax=None)
        self.geoType.setConnection(self)
        return self.geoType 
[docs]    def createLatLongSystem(self, unit='degree', scale=None, latMin=None, latMax=None, longMin=None, longMax=None):
        """
        Create a spherical coordinate system and use it as the current coordinate system.
        :param scale: Estimate of the size of a typical search region in the latitudinal direction.
        :type scale: float|int
        :param unit: One of: ``'degree'``, ``'mile'``, ``'radian'``, ``'km'``.
                     The default is ``'degree'``.
        :type unit: string
        :param longMin: Left side of the coordinate system.
        :type longMin: float|int
        :param longMax: Right side of the coordinate system.
        :type longMax: float|int
        :param latMin: Bottom border of the coordinate system.
        :type latMin: float|int
        :param latMax: Top border of the coordinate system.
        :type latMax: float|int
        :return: The new coordinate system.
        """
        self.geoType = GeoType(GeoType.Spherical, unit=unit, scale=scale, latMin=latMin, latMax=latMax, longMin=longMin, longMax=longMax)
        self.geoType.setConnection(self)
        return self.geoType 
[docs]    def getGeoType(self):
        """
        Get the current geospatial coordinate system.
        :return: A coordinate system.
        :rtype: GeoType
        """
        return self.geoType 
[docs]    def setGeoType(self, geoType):
        """
        Set the current geospatial coordinate system.
        :param geoType: The new coordinate system.
        :type geoType: GeoType
        """
        self.geoType = geoType
        geoType.setConnection(self) 
[docs]    def createCoordinate(self, x=None, y=None, latitude=None, longitude=None):
        """
        Create an x, y or latitude, longitude  coordinate in the current coordinate system.
        Either ``x``, ``y`` or ``latitude``, ``longitude`` must be given.
        :param x: X coordinate of the created point.
        :param y: Y coordinate of the created point.
        :param latitude: Latitude of the created point.
        :param longitude: Longitude of the created point.
        :return: A literal representing the created point.
        :rtype: Literal
        """
        return self.geoType.createCoordinate(x=x, y=y, latitude=latitude, longitude=longitude) 
[docs]    def createBox(self, xMin=None, xMax=None, yMin=None, yMax=None):
        """
        Create a rectangular search region (a box) for geospatial search.
        This method works for both Cartesian and spherical coordinate systems.
        :param xMin: Minimum latitude.
        :type xMin: float|int
        :param xMax: Maximum latitude.
        :type xMax: float|int
        :param yMin: Minimum longitude.
        :type yMin: float|int
        :param yMax: Maximum longitude.
        :type yMax: float|int
        :return: A geospatial literal corresponding to the specified search region.
        :rtype: Literal
        """
        return self.geoType.createBox(xMin=xMin, xMax=xMax, yMin=yMin, yMax=yMax) 
[docs]    def createCircle(self, x, y, radius, unit=None):
        """
        Create a circular search region for geospatial search.
        This method works for both Cartesian and spherical coordinate systems.
        :param x: Latitude of the center.
        :type x: float|int
        :param y: Longitude of the center.
        :type y: float|int
        :param radius: The radius of the circle expressed in the designated ``unit``.
        :type radius: float|int
        :param unit: Unit in which the ``radius`` is expressed.
                     Defaults to the unit assigned to the coordinate system.
                     Legal values are ``"degree"``, ``"radian"``, ``"km"`` and ``"mile"``.
        :type unit: string
        """
        return self.geoType.createCircle(x, y, radius, unit=unit) 
[docs]    def createPolygon(self, vertices, uri=None, geoType=None):
        """
        Define a polygonal region with the specified vertices.
        :param vertices: List of x, y pairs.
        :type vertices: list[(float, float)]
        :param uri: URI under which the polygon will be stored in the repository.
                    If not given a blank node will be used.
        :type uri: URI
        :param geoType: unused
        :return: An object representing the newly created polygon.
        :rtype: GeoPolygon
        """
        del geoType
        return self.geoType.createPolygon(vertices, uri=uri) 
    #############################################################################################
    ## SNA   Social Network Analysis Methods
    #############################################################################################
[docs]    def registerSNAGenerator(self, name, subjectOf=None, objectOf=None, undirected=None, generator_query=None):
        """
        Create a new SNA generator named ``name``.
        If one already exists with the same name - redefine it.
        The edges traversed by the new generator can be defined in one of two ways:
           - Using three lists of predicates to define edges between subjects and
             objects of triples (``subjectOf``, ``objectOf``, ``undirected``).
           - Using a Prolog query (``generator_query``).
        Requires a dedicated session.
        :param name: Name of the new/modified generator.
        :type name: string
        :param subjectOf: Create object to subject edges for these predicates.
        :type subjectOf: list[URI|string]
        :param objectOf: Create subject to object edges for these predicates.
        :type objectOf: list[URI|string]
        :param undirected: Create edges in both directions for these predicates.
        :type undirected: list[URI|string]
        :param generator_query: A Prolog query that generates neighbors given
                                a start node ``?node``.
        """
        miniRep = self._get_mini_repository()
        miniRep.registerSNAGenerator(name, subjectOf=subjectOf, objectOf=objectOf, undirected=undirected,
                                     query=generator_query) 
[docs]    def registerNeighborMatrix(self, name, generator, group_uris, max_depth=2):
        """
        Construct a neighbor matrix named ``name``.
        The generator named ``generator`` is applied to each URI in ``group_uris``,
        computing edges to max depth ``max_depth``.
        Requires a dedicated session.
        :param name: Name that will be used to identify the matrix.
        :type name: string
        :param generator: Name of the SNA generator used to compute the edges.
                          See :meth:`.registerSNAGenerator`.
        :type generator: string
        :param group_uris: Initial list of vertices from which the graph will be created.
        :type group_uris: list[URI|string]
        :param max_depth: Max distance from the initial vertices.
        :type max_depth: int
        """
        miniRep = self._get_mini_repository()
        miniRep.registerNeighborMatrix(name, group_uris, generator, max_depth) 
[docs]    def listFreeTextIndices(self):
        """
        Get the names of currently defined free-text indices.
        :return: List of index names.
        :rtype: list[string]
        """
        return self.mini_repository.listFreeTextIndices() 
[docs]    def createFreeTextIndex(self, name, predicates=None, indexLiterals=None, indexResources=None,
                            indexFields=None, minimumWordSize=None, stopWords=None, wordFilters=None,
                            innerChars=None, borderChars=None, tokenizer=None):
        """
        Create a free-text index with the given parameters.
        .. seealso::
           http://franz.com/agraph/support/documentation/current/http-protocol.html#put-freetext-index
              Documentation of the HTTP endpoint used by this method.
           http://franz.com/agraph/support/documentation/current/text-index.html
              General information about text indexing in AllegroGraph.
        :param name: Name for the new index.
        :type name: string
        :param predicates: A list of predicates to be indexed. If not given all
                           triples will be indexed regardless of predicate.
        :type predicates: list[string|URI]
        :param indexLiterals: Determines which literals to index. It can be``True``
                              (the default), ``False``, or a list of resources,
                              indicating the literal types that should be indexed.
        :type indexLiterals: bool|list[URI]
        :param indexResources: Determines which resources are indexed. It can be
                               ``True``, ``False`` (the default), or ``"short"``,
                               to index only the part of resources after the last
                               slash or hash character.
        :type indexResources: bool|string
        :param indexFields: List of triples fields to index. Can be any combination
                            of ``"subject"``, ``"predicate"``, ``"object"``, and
                            ``"graph"``. The default is ``["object"]``.
        :type indexFields: list[string]
        :param minimumWordSize: Determines the minimum size a word must have to be
                                indexed. The default is 3.
        :type minimumWordSize: int
        :param stopWords: List of words that should not be indexed.
                          When not given, a list of common English words is used.
        :type stopWords: list[string]
        :param wordFilters: List of normalizing filters used to process words
                            before indexing. The list if supported filters can
                            be found here:
                            http://franz.com/agraph/support/documentation/current/text-index.html
        :type wordFilters: list[string]
        :param innerChars: The character set to be used as the constituent characters of a word.
                           Should be a list of character classes. Valid classes are:
                               - `"alpha"``: All (unicode) alphabetic characters.
                               - ``"digit"``: All base-10 digits.
                               - ``"alphanumeric"``: All digits and alphabetic characters.
                               - a single character
                               - a range of characters: A single character, followed by a dash (-)
                                 character, followed by another single character.
        :type innerChars: list[string]
        :param borderChars: The character set to be used as the border characters of indexed words.
                            Uses the same syntax as ``innerChars``.
        :type borderChars: list[string]
        :param tokenizer: Controls the way in which the text is plit into tokens.
                          Can be either ``"default"`` or ``"japaense"``.
                          Note that the ``japaense`` tokenizer ignores ``innerChars``
                          and ``borderChars``.
        :type tokenizer: string
        """
        if predicates: predicates = list(map(uris.asURIString, predicates))
        if isinstance(indexLiterals, list): indexLiterals = list(map(uris.asURIString, indexLiterals))
        self.mini_repository.createFreeTextIndex(name, predicates=predicates, indexLiterals = indexLiterals,
                                                 indexResources=indexResources, indexFields=indexFields,
                                                 minimumWordSize=minimumWordSize, stopWords=stopWords,
                                                 wordFilters=wordFilters, innerChars=innerChars,
                                                 borderChars=borderChars, tokenizer=tokenizer) 
[docs]    def modifyFreeTextIndex(self, name, predicates=None, indexLiterals=None, indexResources=None,
                            indexFields=None, minimumWordSize=None, stopWords=None, wordFilters=None,
                            reIndex=None, innerChars=None, borderChars=None, tokenizer=None):
        """
        Modify parameters of a free-text index.
        :param name: Name of the index to be modified.
        :type name: string
        :param predicates: A list of predicates to be indexed. If not given all
                           triples will be indexed regardless of predicate.
        :type predicates: list[string|URI]
        :param indexLiterals: Determines which literals to index. It can be``True``
                              (the default), ``False``, or a list of resources,
                              indicating the literal types that should be indexed.
        :type indexLiterals: bool|list[URI]
        :param indexResources: Determines which resources are indexed. It can be
                               ``True``, ``False`` (the default), or ``"short"``,
                               to index only the part of resources after the last
                               slash or hash character.
        :type indexResources: bool|string
        :param indexFields: List of triples fields to index. Can be any combination
                            of ``"subject"``, ``"predicate"``, ``"object"``, and
                            ``"graph"``. The default is ``["object"]``.
        :type indexFields: list[string]
        :param minimumWordSize: Determines the minimum size a word must have to be
                                indexed. The default is 3.
        :type minimumWordSize: int
        :param stopWords: List of words that should not be indexed.
                          When not given, a list of common English words is used.
        :type stopWords: list[string]
        :param wordFilters: List of normalizing filters used to process words
                            before indexing. The list if supported filters can
                            be found here:
                            http://franz.com/agraph/support/documentation/current/text-index.html
        :type wordFilters: list[string]
        :param innerChars: The character set to be used as the constituent characters of a word.
                           Should be a list of character classes. Valid classes are:
                               - `"alpha"``: All (unicode) alphabetic characters.
                               - ``"digit"``: All base-10 digits.
                               - ``"alphanumeric"``: All digits and alphabetic characters.
                               - a single character
                               - a range of characters: A single character, followed by a dash (-)
                                 character, followed by another single character.
        :type innerChars: list[string]
        :param borderChars: The character set to be used as the border characters of indexed words.
                            Uses the same syntax as ``innerChars``.
        :type borderChars: list[string]
        :param tokenizer: Controls the way in which the text is plit into tokens.
                          Can be either ``"default"`` or ``"japaense"``.
                          Note that the ``japaense`` tokenizer ignores ``innerChars``
                          and ``borderChars``.
        :type tokenizer: string
        """
        if predicates: predicates = list(map(uris.asURIString, predicates))
        if isinstance(indexLiterals, list): indexLiterals = list(map(uris.asURIString, indexLiterals))
        self.mini_repository.modifyFreeTextIndex(name, predicates=predicates, indexLiterals = indexLiterals,
                                                 indexResources=indexResources, indexFields=indexFields,
                                                 minimumWordSize=minimumWordSize, stopWords=stopWords,
                                                 wordFilters=wordFilters, reIndex=reIndex, innerChars=innerChars,
                                                 borderChars=borderChars, tokenizer=tokenizer) 
[docs]    def deleteFreeTextIndex(self, name):
        """
        Delete a free-text index from the server.
        :param name: Index name.
        :type name: string
        """
        self.mini_repository.deleteFreeTextIndex(name) 
[docs]    def getFreeTextIndexConfiguration(self, name):
        """
        Get the current settings of a free-text index.
        The result will be a dictionary where keys are parameter names
        as used in :meth:`.createFreeTextIndex`.
        :param name: Index name.
        :type name: string
        :return: A dictionary with configuration data, keys are argument
                 names from :meth:`.createFreeTextIndex`.
        :rtype: dict
        """
        value = self.mini_repository.getFreeTextIndexConfiguration(name)
        value["predicates"] = list(map(URI, value["predicates"]))
        if isinstance(value["indexLiterals"], list):
            value["indexLiterals"] = list(map(URI, value["indexLiterals"]))
        return value 
[docs]    def evalFreeTextSearch(self, pattern, infer=False, callback=None, limit=None, offset=None, index=None):
        """
        Return a list of statements for the given free-text pattern search.
        If no index is provided, all indices will be used.
        :param pattern: Search pattern, see
                        http://franz.com/agraph/support/documentation/current/http-protocol.html#get-post-freetext
                        for details.
        :type pattern: string
        :param index: List of indices to query. If not given - query all indices.
        :type index: string|list[string]
        """
        miniRep = self._get_mini_repository()
        return miniRep.evalFreeTextSearch(pattern, index, infer, wrap_callback(callback), limit, offset=offset) 
[docs]    def openSession(self, autocommit=False, lifetime=None, loadinitfile=False):
        """
        Open a session.
        It is not an error to call this when a session is already active on this connection,
        but no new session will be created (i.e. nested sessions are not supported).
        .. seealso::
           http://franz.com/agraph/support/documentation/current/http-protocol.html#sessions
              More detailed explanation of session-related concepts in the HTTP API reference.
        :param autocommit: if ``True``, commits are done on each request, otherwise
                           you will need to call :meth:`.commit` or :meth:`.rollback`
                           as appropriate for your application.
                           The default value is ``False``.
        :type autocommit: bool
        :param lifetime: Time (in seconds) before the session expires when idle.
                         Note that the client maintains a thread that pings the
                         session before this happens.
                         The maximum acceptable value is 21600 (6 hours).
                         When the value is ``None`` (the default) the lifetime
                         is set to 300 seconds (5 minutes).
        :type lifetime: int
        :param loadinitfile: if ``True`` then the current initfile will be loaded
                             for you when the session starts. The default is ``False``.
        :type loadinitfile: bool
        """
        if not self.is_session_active:
            miniRep = self._get_mini_repository()
            if miniRep == self.repository.mini_repository:
                # Don't use the shared mini_repository for a session
                miniRep = self.mini_repository = copy.copy(self.repository.mini_repository)
            miniRep.openSession(autocommit, lifetime, loadinitfile)
            self.is_session_active = True 
[docs]    def closeSession(self):
        """
        Close a session.
        It is not an error to call this when no session is active.
        """
        if self.is_session_active:
            # Not deleting the dedicated mini_repository in case the user
            # calls openSession again.
            self._get_mini_repository().closeSession()
            self.is_session_active = False 
    def __enter__(self):
        return self
    def __exit__(self, *args):
        del args
        self.close()
[docs]    @contextmanager
    def session(self,  autocommit=False, lifetime=None, loadinitfile=False):
        """
        A session context manager for use with the ``with`` statement:
        .. code:: python
            with conn.session():
                # Automatically calls openSession at block start
                # Do work
                # Automatically calls closeSession at block end
                # or in case of an exception.
        :param autocommit: if ``True``, commits are done on each request, otherwise
                           you will need to call :meth:`.commit` or :meth:`.rollback`
                           as appropriate for your application.
                           The default value is ``False``.
        :type autocommit: bool
        :param lifetime: Time (in seconds) before the session expires when idle.
                         Note that the client maintains a thread that ping the
                         session before this happens.
        :type lifetime: int
        :param loadinitfile: if ``True`` then the current initfile will be loaded
                             for you when the session starts. The default is ``False``.
        :type loadinitfile: bool
        """
        self.openSession(autocommit, lifetime, loadinitfile)
        yield self
        self.closeSession() 
[docs]    def runAsUser(self, username=None):
        """
        Only for use as a superuser during a session.
        Runs requests on this connection as username.
        None - the default - clears the setting.
        """
        return self._get_mini_repository().runAsUser(username) 
    # This is completely insane. Are we really suggesting that to the users!?
[docs]    @contextmanager
    def saveResponse(self, fileobj, accept, raiseAll=False):
        """
        Save the server response(s) for the call(s) within the with statement
        to fileobj, using accept for the response type requested.
        .. deprecated:: 100.0.1
           Use getStatements(output=...) or evaluate(output=...) to export data.
        Responses will be uncompressed and saved to fileobj, but not decoded.
        Therefore, the API called will likely error out shortly after the
        response is saved (which is okay because we really only want
        the side-effect of saving the response).
        RequestError is always thrown on errors from the server.
        Other exceptions can be optionally raised with raiseAll=True.
        You will only want to make only one conn call in the with statement
        unless you wrap each call in its own try/except.
        Example:
        with open('out', 'w') as response:
            with conn.saveResponse(response, 'application/rdf+xml'):
                conn.getStatements(None, None, None) # The response is written to response
        """
        with self._get_mini_repository().saveResponse(fileobj, accept, raiseAll):
            yield 
[docs]    def commit(self):
        """
        Commit changes on an open session.
        """
        return self._get_mini_repository().commit() 
[docs]    def rollback(self):
        """
        Roll back changes on open session.
        """
        return self._get_mini_repository().rollback() 
[docs]    def evalInServer(self, code):
        """
        Evaluate the Lisp code in the server.
        You must have "eval" permissions to the store to use this feature.
        """
        return self._get_mini_repository().evalInServer(code) 
[docs]    def evalJavaScript(self, code):
        """
        Evaluate the JavaScript code in the server.
        You must have "eval" permissions to the store to use this feature.
        """
        return self._get_mini_repository().evalJavaScript(code) 
    # NOTE: 'format' shadows a built-in symbol but it is too late to change the public API
[docs]    def registerEncodedIdPrefix(self, prefix, format):
        """
        Registers a single encoded prefix.
        See: https://franz.com/agraph/support/documentation/current/encoded-ids.html
        """
        return self._get_mini_repository().registerEncodedIdPrefix(prefix, format) 
[docs]    def registerEncodedIdPrefixes(self, registrations):
        """
        Registers multiple encoded prefixes. Any kind of iteratable collection
        of items with a prefix attribute and format attribute, or the prefix
        at index 0 and the format at index 1 will do (e.g. a list of tuples).
        Using PrefixFormat instances also works well.
        See: https://franz.com/agraph/support/documentation/current/encoded-ids.html
        """
        return self._get_mini_repository().registerEncodedIdPrefixes(registrations) 
[docs]    def listEncodedIdPrefixes(self):
        """
        Lists all encoded id prefixes.
        See: https://franz.com/agraph/support/documentation/current/encoded-ids.html
        """
        regs = []
        enc_ids = self._get_mini_repository().listEncodedIdPrefixes()
        for enc_id in enc_ids:
            regs.append(PrefixFormat(enc_id["prefix"], enc_id["format"]))
        return regs 
[docs]    def unregisterEncodedIdPrefix(self, prefix):
        """
        Unregisters the specified encoded id prefix.
        See: https://franz.com/agraph/support/documentation/current/encoded-ids.html
        """
        return self._get_mini_repository().unregisterEncodedIdPrefix(prefix) 
[docs]    def allocateEncodedIds(self, prefix, amount=1):
        """
        allocateEncodedIds allows you to use the server to allocate
        unique ids for a given registered prefix which has a fixed-width
        format specifier.
        See notes on next-encoded-upi-for-prefix in:
        https://franz.com/agraph/support/documentation/current/encoded-ids.html
        """
        return self._get_mini_repository().allocateEncodedIds(prefix, amount) 
[docs]    def deleteDuplicates(self, mode):
        """
        Delete duplicate triples from the store.
        :param mode: can be `"spo"` (triples are duplicates if they have the same subject,
                    predicate, and object, regardless of the graph) or `"spog"` (triples
                    are duplicates if they have the same subject, predicate, object, and
                    graph).
        .. seealso::
           Method :meth:`getDuplicateStatements`.
        """
        self._get_mini_repository().deleteDuplicates(mode) 
[docs]    def getDuplicateStatements(self, mode):
        """
        Return all duplicates in the store.
        :param mode: can be `"spo"` (triples are duplicates if they have the same subject,
            predicate, and object, regardless of the graph) or `"spog"` (triples
            are duplicates if they have the same subject, predicate, object, and
            graph).
        :return: An iterator over duplicate statements.
        :rtype: RepositoryResult
        """
        stringTuples = self._get_mini_repository().getDuplicateStatements(mode)
        return RepositoryResult(stringTuples) 
[docs]    def getDuplicateSuppressionPolicy(self):
        """
        Return the duplicate suppression policy used by the store.
        See https://franz.com/agraph/support/documentation/current/deleting-duplicate-triples.html
        :return: Duplicate suppression policy: "spo", "spog" or None.
        :rtype: str
        """
        return self._get_mini_repository().getDuplicateSuppressionPolicy() 
[docs]    def setDuplicateSuppressionPolicy(self, mode):
        """
        Set the duplicate suppression policy used by the store.
        See https://franz.com/agraph/support/documentation/current/deleting-duplicate-triples.html
        :param mode: Duplicate suppression policy: "spo", "spog" or None (disable suppression).
        :type mode: str
        """
        self._get_mini_repository().setDuplicateSuppressionPolicy(mode) 
[docs]    def disableDuplicateSuppression(self):
        """
        Disable duplicate suppression for this store.
        See https://franz.com/agraph/support/documentation/current/deleting-duplicate-triples.html
        """
        self._get_mini_repository().disableDuplicateSuppression() 
[docs]    def callStoredProc(self, function, module, *args):
        return self._get_mini_repository().callStoredProc(function, module,
            *args) 
[docs]    def getSpinFunction(self, uri):
        """
        Gets the string of the function for the given uri.
         uri - Spin function identifier
        """
        return self._get_mini_repository().getSpinFunction(uri) 
[docs]    def putSpinFunction(self, uri, sparqlQuery, arguments):
        """
        Adds a Spin function.
         uri - Spin function identifier
         sparqlQuery - Spin function query text
         arguments - names of arguments in the sparqlQuery
        """
        self._get_mini_repository().putSpinFunction(uri, sparqlQuery, arguments) 
[docs]    def deleteSpinFunction(self, uri):
        """
        Deletes the Spin function at the given uri.
         uri - Spin function identifier
        """
        return self._get_mini_repository().deleteSpinFunction(uri) 
[docs]    def listSpinFunctions(self):
        """
        Returns a list of defined SPIN function.
        """
        return self._get_mini_repository().listSpinFunctions() 
[docs]    def putSpinMagicProperty(self, uri, sparqlQuery, arguments):
        """
        Add a Spin magic property.
         uri - Spin magic property identifier
         sparqlQuery
         arguments - names of arguments to the sparqlQuery - must contain the leading question mark
        """
        self._get_mini_repository().putSpinMagicProperty(uri, sparqlQuery, arguments) 
[docs]    def getSpinMagicProperty(self, uri):
        """
        Get the spin magic property for the uri
         uri - spin magic property identifier
        """
        return self._get_mini_repository().getSpinMagicProperty(uri) 
[docs]    def listSpinMagicProperties(self):
        """
        Returns a list of defined SPIN magic properties function.
        """
        return self._get_mini_repository().listSpinMagicProperties() 
[docs]    def deleteSpinMagicProperty(self, uri):
        """
        Deletes the Spin magic property at the given uri.
         uri - Spin magic property identifier
        """
        self._get_mini_repository().deleteSpinMagicProperty(uri) 
[docs]    def materializeEntailed(self, _with=None, without=None, useTypeSubproperty=False, commit=100000):
        """
        Call to materialize entailed triples to enable reasoning queries without the dynamic query-time reasoner.
        Returns the number of triples added.
        _with and without can be either a single string or a list of strings denoting rules beyond rdfs++ you wish to use.
        See the documentation for the current set, but "same-as", "restriction", "values-from", "class", and "property" are examples.
        useTypeSubproperty tells the materializer to prefer using types that are rdfs:subPropertyOf rdf:type rather than rdf:type directly.
        commit indicates the number of triples per commit for the materializer.
        """
        return self._get_mini_repository().materializeEntailed(_with=_with, without=without, useTypeSubproperty=useTypeSubproperty, commit=commit) 
[docs]    def deleteMaterialized(self):
        """
        Deletes all previously materialized triples.
        Returns the number of triples deleted.
        """
        return self._get_mini_repository().deleteMaterialized() 
[docs]    def namespace(self, prefix):
        """
        Creates an object that allows for simple creation of URIs in given namespace.
        Attribute lookups on the returned object will produce URIs with the attribute
        name as localname.
        >>> from franz.openrdf.connect import ag_connect
        >>> conn = ag_connect('repo')
        >>> ex = conn.namespace('http://franz.com/example/')
        >>> ex.foo
        <http://franz.com/example/foo>
        :param prefix: Prefix prepended to URIs created by the returned object.
        :type prefix: str
        :return: An object that can be used to create URIs.
        """
        return self.getValueFactory().namespace(prefix) 
[docs]    def executeTupleQuery(self, query, language=QueryLanguage.SPARQL,
                          output=None, output_format=RDFFormat.TABLE):
        """
        Prepare and immediately evaluate a query that returns tuples.
        :param query: Query text.
        :type query: str
        :param language: Query language, the default is SPARQL.
        :type language: QueryLanguage
        :param output: File path or a file-like object to write
                       the result to.
        :type output: str|file
        :param output_format: Serialization format for ``output``.
        :type output_format: RDFFormat
        :return: Query result, or ``None`` if ``output`` is used.
        :rtype: TupleQueryResult
        """
        q = self.prepareTupleQuery(language, query)
        return q.evaluate(output=output, output_format=output_format) 
[docs]    def executeGraphQuery(self, query, language=QueryLanguage.SPARQL,
                          output=None, output_format=RDFFormat.NQX):
        """
        Prepare and immediately evaluate a query that returns RDF.
        :param query: Query text.
        :type query: str
        :param language: Query language, the default is SPARQL.
        :type language: QueryLanguage
        :param output: File path or a file-like object to write
                       the result to.
        :type output: str|file
        :param output_format: Serialization format for ``output``.
        :type output_format: RDFFormat
        :return: Query result, or ``None`` if ``output`` is used.
        :rtype: RepositoryResult|None
        """
        q = self.prepareGraphQuery(language, query)
        return q.evaluate(output=output, output_format=output_format) 
[docs]    def executeBooleanQuery(self, query, language=QueryLanguage.SPARQL):
        """
        Prepare and immediately evaluate a query that returns
        a boolean.
        :param query: Query text.
        :type query: str
        :param language: Query language, the default is SPARQL.
        :type language: QueryLanguage
        :return: Query result.
        :rtype: bool
        """
        q = self.prepareBooleanQuery(language, query)
        return q.evaluate() 
[docs]    def executeUpdate(self, query):
        """
        Prepare and immediately evaluate a SPARQL update query.
        :param query: Query text.
        :type query: str
        :return: Query result (true iff the store has been modified).
        :rtype: bool
        """
        q = self.prepareUpdate(QueryLanguage.SPARQL, query)
        return q.evaluate()  
    
[docs]class GeoType(object):
    Cartesian = 'CARTESIAN'
    Spherical = 'SPHERICAL'
[docs]    def __init__(self, system, scale=None, unit=None, xMin=None, xMax=None, yMin=None, yMax=None, latMin=None, latMax=None, longMin=None, longMax=None):
        self.system = system
        self.connection = None
        self.scale = scale
        self.unit = unit
        self.xMin = xMin
        self.xMax = xMax
        self.yMin = yMin
        self.yMax = yMax
        self.latMin = latMin
        self.latMax = latMax
        self.longMin = longMin
        self.longMax = longMax
        self.miniGeoType = None
        if system == GeoType.Cartesian:
            if unit:
                raise Exception("Units not yet supported for rectangular coordinates.")
        else: pass  ## can't happen 
[docs]    def setConnection(self, connection): self.connection = connection 
    def _getMiniGeoType(self):
        def stringify(term): return unicode(term) if term is not None else None
        if not self.miniGeoType:
            if self.system == GeoType.Cartesian:
                self.miniGeoType = self.connection._get_mini_repository().getCartesianGeoType(stringify(self.scale), stringify(self.xMin), stringify(self.xMax),
                                                                                stringify(self.yMin), stringify(self.yMax))
            elif self.system == GeoType.Spherical:
                self.miniGeoType = self.connection._get_mini_repository().getSphericalGeoType(stringify(self.scale), unit=stringify(self.unit),
                                latMin=stringify(self.latMin), latMax=stringify(self.latMax), longMin=stringify(self.longMin), longMax=stringify(self.longMax))
        return self.miniGeoType
[docs]    def createCoordinate(self, x=None, y=None, latitude=None, longitude=None, unit=None):
        """
        Create an x, y  or lat, long  coordinate for the system defined by this geotype.
        """
        return GeoCoordinate(x=(x or latitude), y=y or longitude, unit=unit, geoType=self) 
[docs]    def createBox(self, xMin=None, xMax=None, yMin=None, yMax=None, unit=None):
        """
        Define a rectangular region for the current coordinate system.
        """
        return GeoBox(xMin, xMax, yMin, yMax, unit=unit, geoType=self) 
[docs]    def createCircle(self, x, y, radius, unit=None):
        """
        Define a circular region with vertex x,y and radius "radius".
        The distance unit for the radius is either 'unit' or the unit specified
        for this GeoType.  If 'innerRadius' is specified, the region is
        ring-shaped, consisting of the space between the inner and outer circles.
        """
        return GeoCircle(x, y, radius, unit=unit, geoType=self) 
[docs]    def createPolygon(self, vertices, uri=None):
        """
        Define a polygonal region with the specified vertices.  'vertices'
        is a list of x,y pairs. The 'uri' is optional.
        """
        poly = GeoPolygon(vertices, uri=uri, geoType=self)
        poly.resource = self.connection.createURI(uri) if uri else self.connection.createBNode()
        miniResource = self.connection._convert_term_to_mini_term(poly.resource)
        miniRep = self.connection._get_mini_repository()
        if self.system == GeoType.Cartesian:
            miniVertices = [miniRep.createCartesianGeoLiteral(self._getMiniGeoType(), coord[0], coord[1]) for coord in poly.vertices]
        elif self.system == GeoType.Spherical:
            miniVertices = [miniRep.createSphericalGeoLiteral(self._getMiniGeoType(), coord[0], coord[1]) for coord in poly.vertices]
        miniRep.createPolygon(miniResource, miniVertices)
        return poly