########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Server/Server/Xslt/Model.py,v 1.87 2005/04/06 23:05:46 jkloth Exp $
"""
XSLT and XPath extensions supporting the 4Suite RDF API

Copyright 2004 Fourthought, Inc. (USA).
Detailed license and copyright information: http://4suite.org/COPYRIGHT
Project home, documentation, distributions: http://4suite.org/
"""

import cStringIO, time
from string import strip

from Ft.Lib import Time as FtTime
from Ft.Rdf import Model
from Ft.Rdf import OBJECT_TYPE_UNKNOWN, OBJECT_TYPE_LITERAL, OBJECT_TYPE_RESOURCE
from Ft.Rdf.Statement import Statement
from Ft.Rdf.Parsers import Versa
from Ft.Rdf.Triclops import ParseNsMappings
from Ft.Server import RESERVED_NAMESPACE
from Ft.Server.Common.ResourceTypes import *
from Ft.Xml.XPath import Conversions, Util
from Ft.Xml.Xslt import XsltElement, ContentInfo, AttributeInfo

from Ns import RDF_NS
import FtssXsltBase

VB_PARAM = (RESERVED_NAMESPACE, 'versa-variable-bindings')

OBJ_TYPE_LOOKUP = {'RESOURCE': OBJECT_TYPE_RESOURCE,
                   'LITERAL': OBJECT_TYPE_LITERAL}


def _serializeStatements(stmts, writer):
    """
    Use a DOM writer to serialize a given list of statements as
    returned from a complete. Used by various functions in this
    module.
    """
    for stmt in stmts:
        writer.startElement(u'Statement')
        writer.startElement(u'Subject')
        writer.text(stmt.subject)
        writer.endElement(u'Subject')
        writer.startElement(u'Predicate')
        writer.text(stmt.predicate)
        writer.endElement(u'Predicate')
        writer.startElement(u'Object')
        writer.text(stmt.object)
        writer.endElement(u'Object')
        writer.startElement(u'StatementUri')
        writer.text(stmt.uri)
        writer.endElement(u'StatementUri')
        writer.startElement(u'Scope')
        writer.text(stmt.scope)
        writer.endElement(u'Scope')
        writer.endElement(u'Statement')

# -- extension functions ----------------------------------------------

def RdfComplete(context, subj, pred, obj=None):
    """
    Runs a complete on the repository model, which matches patterns
    among the triples in the model.  You provide whichever of the
    subject, predicate and object you'd like to match, and use
    the frdf:any() function as a wildcard.  The return value is a generated
    node set which has one or more "Statement" elements, each with 5
    children: "Subject", "Predicate", "Object", "StatementUri", "Scope"
    which represent the respective components of the RDF statement.

    for example:

    frdf:complete("http://spam.com", frdf:any(), frdf:any())

    would return all statements with "http://spam.com" as the subjects.
    """
    repo = context.processor._repository

    model = repo.getModel()
    subj = not not subj and Conversions.StringValue(subj) or None
    pred = not not pred and Conversions.StringValue(pred) or None
    if obj is not None: obj = Conversions.StringValue(obj)
    proc = context.processor
    #print repr(subj), repr(pred), repr(obj)
    stmts = model.complete(subj, pred, obj)
    #print stmts
    proc.pushResultTree(context.currentInstruction.baseUri)
    try:
        _serializeStatements(stmts, proc.writers[-1])
    finally:
        rtf = proc.popResult()
    return rtf.childNodes


def RdfAny(context):
    return None


def RdfContains(context, subj, pred, obj=None):
    """
    Runs a conatins on the repository model, which matches patterns
    among the triples in the model.  You provide whichever of the
    subject, predicate and object you'd like to match, and use
    the frdf:any() function as a wildcard.  The return value is a boolean
    indicating whether or not a matching statement exists.
    """
    # could be None from frdf:any()
    subj = subj and Conversions.StringValue(subj)
    pred = pred and Conversions.StringValue(pred)
    obj = obj and Conversions.StringValue(obj)

    model = context.processor._repository.getModel()
    result = model.containsPattern(subj, pred, obj)
    return Conversions.BooleanValue(result)


def RdfVersaQueryFunction(context, query, scope=None):
    """Perform a Versa Query as a function, return a node-set representing the outside list."""

    processor = context.processor

    query = Conversions.StringValue(query)
    if scope: scope = Conversions.StringValue(scope)
    try:
        print "Compiling Versa: %r" % query
        exp = Versa.Compile(query)
    except:
        import traceback
        st = cStringIO.StringIO()
        traceback.print_exc(file=st)
        processor.xslMessage(st.getvalue())
        raise

    repo = processor._repository
    var_bindings = context.processor.extensionParams.get(VB_PARAM, {})
    con = Versa.CreateContext(model=repo.getModel(),
                              nsMapping=context.processorNss,
                              varBindings=var_bindings,
                              scope=scope)
    try:
        print "Evaluating Query"
        now = time.time()
        results = exp.evaluate(con)
        print "Time to evaluate: ", time.time() - now
    except:
        #Error
        import traceback
        st = cStringIO.StringIO()
        traceback.print_exc(file=st)
        processor.xslMessage(st.getvalue())
        raise
    #print results

    # Use an RTF to create a node-set
    processor.pushResultTree(context.currentInstruction.baseUri)
    try:
        WriteVersaResults(processor, results)
    finally:
        rtf = processor.popResult()
    return [rtf]


# -- extension elements -----------------------------------------------


class RdfBaseElement(XsltElement):

    content = ContentInfo.Empty

    #FIXME: Make type an enum, not any AVT
    legalAttrs = {
        's' : AttributeInfo.Expression(description='The subject'),
        'p' : AttributeInfo.Expression(description='The predicate'),
        'o' : AttributeInfo.Expression(description='The object'),
        'type' : AttributeInfo.Expression(
            description='The type (or xml:lang) of the object'),
        'scope' : AttributeInfo.StringExpression(
            description='The scope of the statement'),
        'stmt-uri' : AttributeInfo.StringExpression(
            description='The statement uri'),
        }

    def getStatementInfo(self, context):
        if self._s:
            subj = self._s.evaluate(context)
            if isinstance(subj, list):
                subjects = map(lambda x: Conversions.StringValue(x), subj)
            else:
                subjects = [Conversions.StringValue(subj)]
        else:
            subjects = [None]

        if self._p:
            pred = self._p.evaluate(context)
            if isinstance(pred, list):
                predicates = map(lambda x: Conversions.StringValue(x), pred)
            else:
                predicates = [Conversions.StringValue(pred)]
        else:
            predicates = [None]

        if self._o:
            obj = self._o.evaluate(context)
            if isinstance(obj, list):
                objects = map(lambda x: Conversions.StringValue(x), obj)
            else:
                objects = [Conversions.StringValue(obj)]
        else:
            objects = [None]

        if self._type:
            type_ = OBJ_TYPE_LOOKUP[self._type.evaluate(context)]
        else:
            type_ = OBJECT_TYPE_UNKNOWN

        if self._stmt_uri:
            statementUri = Conversions.StringValue(self._stmt_uri.evaluate(context))
        else:
            statementUri = ''

        if self._scope:
            scope = Conversions.StringValue(self._scope.evaluate(context))
        else:
            scope = ''
        return (subjects, predicates, objects, scope, statementUri, type_)


class RdfPatternBaseElement(RdfBaseElement):

    legalAttrs = RdfBaseElement.legalAttrs.copy()
    legalAttrs.update({
        's-flags' : AttributeInfo.ChoiceAvt(
            ['normal', 'regex', 'ignore-case', 'regex-ignore-case'],
            default='normal',
            description=('Indicates the method for matching the value of'
                         ' the subject argument')),
        'p-flags' : AttributeInfo.ChoiceAvt(
            ['normal', 'regex', 'ignore-case', 'regex-ignore-case'],
            default='normal',
            description=('Indicates the method for matching the value of'
                         ' the predicate argument')),
        'o-flags' : AttributeInfo.ChoiceAvt(
            ['normal', 'regex', 'ignore-case', 'regex-ignore-case'],
            default='normal',
            description=('Indicates the method for matching the value of'
                         ' the object argument')),
        'scope-flags' : AttributeInfo.ChoiceAvt(
            ['normal', 'regex', 'ignore-case', 'regex-ignore-case'],
            default='normal',
            description=('Indicates the method for matching the value of'
                         ' the scope argument')),
        'stmt-uri-flags' : AttributeInfo.ChoiceAvt(
            ['normal', 'regex', 'ignore-case', 'regex-ignore-case'],
            default='normal',
            description=('Indicates the method for matching the value of'
                         ' the statement URI argument')),
        })

    def getRdfPatternCriteria(self, context):

        (subj, pred, obj, scope, stmtUri, t) = self.getStatementInfo(context)

        criteria = []
        for s in subj:
            for p in pred:
                for o in obj:
                    criteria.append((s, p, o))

        flag_dict = {}
        if stmtUri:
            flag_dict['statementUri'] = stmtUri
        if scope:
            flag_dict['scope'] = scope

        flag_val = {'normal' : Model.NORMAL,
                    'regex' : Model.REGEX,
                    'ignore-case' : Model.IGNORE_CASE,
                    'regex-ignore-case' : Model.REGEX | Model.IGNORE_CASE,
                    }
        flags = self._s_flags.evaluate(context)
        flag_dict['subjectFlags'] = flag_val[flags]

        flags = self._p_flags.evaluate(context)
        flag_dict['predicateFlags'] = flag_val[flags]

        flags = self._o_flags.evaluate(context)
        flag_dict['objectFlags'] = flag_val[flags]

        flags = self._scope_flags.evaluate(context)
        flag_dict['scopeFlags'] = flag_val[flags]

        flags = self._stmt_uri_flags.evaluate(context)
        flag_dict['statementUriFlags'] = flag_val[flags]

        return criteria, flag_dict


class RdfCompleteElement(RdfPatternBaseElement):

    def instantiate(self, context, processor):
        context.setProcessState(self)

        criteria, flag_dict = self.getRdfPatternCriteria(context)
        #print criteria, flag_dict

        repo = context.processor._repository
        model = repo.getModel()

        stmts = []
        for c in criteria:
            subset = model.complete(*c, **flag_dict)
            stmts.extend(subset)
            #print stmts

        _serializeStatements(stmts, processor.writers[-1])

        return


class RdfRemoveElement(RdfPatternBaseElement):
    """
    Removes statements to the repository model.  The subject,
    predicates and objects can be strings or node sets
    node sets are converted to a list of strings by taking
    the string value of each item.  String values are treated
    a list with one string.  The cartesian product of
    statements that come from the list of subject, predicate
    and object strings and that match the given scope and
    statement URI are all removed from the model.
    """

    def instantiate(self, context, processor):
        context.setProcessState(self)

        criteria, flag_dict = self.getRdfPatternCriteria(context)

        repo = context.processor._repository
        model = repo.getModel()

        for c in criteria:
            model.removePattern(*c, **flag_dict)

        return


class RdfAddElement(RdfBaseElement):
    """
    Adds statements to the repository model.  The subject,
    predicates and objects can be strings or node sets
    node sets are converted to a list of strings by taking
    the string value of each item.  String values are treated
    a list with one string.  The cartesian product of
    statements that come from the list of subject, predicate
    and object strings are all added to the model using the
    given scope and statement URI.
    """

    def instantiate(self, context, processor):
        context.setProcessState(self)

        subj, pred, obj, scope, stmtUri, typ = self.getStatementInfo(context)

        stmts = []
        for s in subj:
            for p in pred:
                for o in obj:
                    stmts.append(Statement(s, p, o, stmtUri, scope,
                                           objectType=typ))

        repo = context.processor._repository
        model = repo.getModel()
        model.add(stmts)
        return


class RdfVersaAssignElement(XsltElement):
    """
    Execute a Versa query and assign the result to a Versa variable,
    for use in further queries.  The variable becomes bound in the
    Versa context for the current stylesheet invokation, and thus will
    be defined on all Versa queries performed subsequently, through
    <frdf:versa-query> or <frdf:versa-assign>.

    The content of the frdf:versa-assign element is a template for the
    extra namespace mappings for the query (see /ftss/dashboard/NsMappings.xml
    for an example of the xml format).

    For example, if you have the following:

    <frdf:versa-assign query="type(rdfs:Class)" name="classes"/>

    You can later execute

    <frdf:versa-query query="$classes - rdfs:label -> *"/>
    """

    content = ContentInfo.Template
    legalAttrs = {
        'query' : AttributeInfo.StringAvt(
            required=1, description='The Versa query string'),
        'name' : AttributeInfo.QNameAvt(
            required=1, description=('Name of the Versa variable to which to'
                                     ' assign results of this query')),
        'scope' : AttributeInfo.StringAvt(
            description='Narrow the versa query to the given scope'),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)
        queryString = self._query.evaluate(context)
        ename = self._name.evaluate(context)
        scope = self._scope.evaluate(context)

        print "Compiling Versa query: %s" % queryString
        try:
            exp = Versa.Compile(queryString)
        except:
            #Error
            import traceback
            st = cStringIO.StringIO()
            traceback.print_exc(file=st)
            processor.xslMessage(st.getvalue())
            raise

        if self.children:
            processor.pushResultTree(self.baseUri)
            self.processChildren(context, processor)
            node = processor.popResult()
            newMappings = ParseNsMappings(node, self.namespaces)
            self.namespaces.update(newMappings)

        repo = context.processor._repository
        if not context.processor.extensionParams.has_key(VB_PARAM):
            context.processor.extensionParams[VB_PARAM] = {}
        var_bindings = context.processor.extensionParams[VB_PARAM]
        con = Versa.CreateContext(model=repo.getModel(),
                                  nsMapping=self.namespaces,
                                  varBindings=var_bindings,
                                  scope=scope)

        print "Evaluating Query for Assignment"
        try:
            now = time.time()
            results = exp.evaluate(con)
            print "Time to evaluate: ", time.time() - now
        except:
            #Error
            import traceback
            st = cStringIO.StringIO()
            traceback.print_exc(file=st)
            processor.xslMessage(st.getvalue())
            raise
        print "Finished evaluating query"

        context.processor.extensionParams[VB_PARAM][ename] = results
        return

    def _writeResults(self, processor, results):
        WriteVersaResults(processor, results)
        return


class RdfVersaQueryElement(XsltElement):
    """
    Execute a Versa query and put the results in serialized XML form to the
    XSLT processor output.

    The content of the frdf:versa-query element is a template for the
    extra namespace mappings for the query (see /ftss/dashboard/NsMappings.xml
    for an example of the xml format).

    The form of the Versa output is as documented in
    http://rdfinference.org/versa-xml.
    """
    content = ContentInfo.Template
    legalAttrs = {
        'query' : AttributeInfo.StringAvt(
            required=1, description='The query string'),
        'scope' : AttributeInfo.StringAvt(
            description='Narrow the versa query to the given scope'),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)

        queryString = self._query.evaluate(context)
        scope = self._scope.evaluate(context)
        repo = context.processor._repository
        print "Compiling Versa: %s" % queryString
        try:
            exp = Versa.Compile(queryString)
        except:
            #Error
            import traceback
            st = cStringIO.StringIO()
            traceback.print_exc(file=st)
            processor.xslMessage(st.getvalue())
            raise

        if self.children:
            processor.pushResultTree(self.baseUri)
            self.processChildren(context, processor)
            node = processor.popResult()
            newMappings = ParseNsMappings(node, self.namespaces)
            self.namespaces.update(newMappings)

        #add dashboard namemapping mappings
        #newMappings = ParseNsMappings(repo.fetchResource('/ftss/dashboard/NsMappings.xml').getContent(), {})
        #self.namespaces.update(newMappings)

        var_bindings = context.processor.extensionParams.get(VB_PARAM, {})
        con = Versa.CreateContext(model=repo.getModel(),
                                  nsMapping=self.namespaces,
                                  varBindings=var_bindings,
                                  scope=scope)

        print "Evaluating Query"
        try:
            now = time.time()
            results = exp.evaluate(con)
            print "Time to evaluate: ", time.time() - now
        except:
            #Error
            import traceback
            st = cStringIO.StringIO()
            traceback.print_exc(file=st)
            processor.xslMessage(st.getvalue())
            raise
        print "Finished evaluating query"

        self._writeResults(processor, results)
        return

    def _writeResults(self, processor, results):
        WriteVersaResults(processor, results)
        return


from Ft.Rdf import Model
DUMMY_MODEL = Model.Model(None)
from Ft.Rdf.Parsers.Versa import DataTypes
def WriteVersaResults(processor, results):
    # By giving a namespace, the elements are created in the null-namespace
    writer = processor.writers[-1]
    if DataTypes.IsList(results):
        writer.startElement(u'List')
        for result in results:
            WriteVersaResults(processor, result)
        writer.endElement(u'List')
    elif DataTypes.IsSet(results):
        writer.startElement(u'Set')
        for result in DataTypes.ToList(results):
            WriteVersaResults(processor, result)
        writer.endElement(u'Set')
    elif DataTypes.IsResource(results):
        if DUMMY_MODEL.isBnodeLabel(str(results)):
            elem = u'BlankNode'
        else:
            elem = u'Resource'
        writer.startElement(elem)
        writer.text(unicode(results))
        writer.endElement(elem)
    elif DataTypes.IsString(results):
        writer.startElement(u'String')
        writer.text(unicode(results))
        writer.endElement(u'String')        
    elif DataTypes.IsNumber(results):
        writer.startElement(u'Number')
        writer.text(unicode(results))
        writer.endElement(u'Number')
    elif DataTypes.IsBoolean(results):
        writer.startElement(u'Boolean')
        writer.text(unicode(results))
        writer.endElement(u'Boolean')
    else:
        raise TypeError('Unknown Versa result type %s' % type(results))


class DeserializeAndAddElement(XsltElement):
    """
    Deserializes an RDF document into statements, and adds them to a
    scope

    The content of the frdf:deserialize-and-add element is a template for the
    RDF/XML source from which the statements are parsed.
    """

    content = ContentInfo.Template

    legalAttrs = {
        'path' : AttributeInfo.UriReferenceAvt(
            required=1, description=('The path of the RDF resource in the'
                                     ' repository')),
        'base-path' : AttributeInfo.UriReferenceAvt(
            description='The base to use for relative paths'),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)

        path = self._path.evaluate(context)
        basePath = self._base_path.evaluate(context)

        stream = cStringIO.StringIO()
        params = processor.writers[-1]._outputParams.clone()
        params.method = (None, u'xml')
        processor.addHandler(params, stream)
        self.processChildren(context, processor)
        processor.removeHandler()

        enc = processor.writers[-1]._outputParams.encoding or 'utf-8'
        text = unicode(stream.getvalue(), enc)

        print "Deserialize RDF"
        #print "Scope: %s" % scope

        if not basePath:
            basePath = processor.extensionParams.get((RESERVED_NAMESPACE, 'path'), '/')
        base = FtssXsltBase.FetchBaseObject(processor, basePath)
        obj = base.fetchResource(path)
        obj.deserializeAndAdd(text)
        return


class DeserializeAndRemoveElement(XsltElement):
    """
    Deserializes an RDF document into statements, and removes any statements
    in the given scope with matching triple values.

    The content of the frdf:deserialize-and-remove element is a template for
    the RDF/XML source from which the statements are parsed.
    """

    content = ContentInfo.Template

    legalAttrs = {
        'path' : AttributeInfo.UriReferenceAvt(
            required=1, description=('The path of the RDF resource in the'
                                     ' repository')),
        'base-path' : AttributeInfo.UriReferenceAvt(
            description='The base to use for relative paths'),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)

        path = self._path.evaluate(context)
        basePath = self._base_path.evaluate(context)

        stream = cStringIO.StringIO()
        params = processor.writers[-1]._outputParams.clone()
        params.method = (None, u'xml')
        processor.addHandler(params, stream)
        self.processChildren(context, processor)
        processor.removeHandler()

        enc = processor.writers[-1]._outputParams.encoding or 'utf8'
        text = unicode(stream.getvalue(), enc)

        print "Deserialize RDF"
        print "PATH: %s" % path

        if not basePath:
            basePath = processor.extensionParams.get((RESERVED_NAMESPACE, 'path'), '/')
        base = FtssXsltBase.FetchBaseObject(processor, basePath)
        obj = base.fetchResource(path)
        obj.deserializeAndRemove(text)
        return


class RdfVisualizeElement(XsltElement):
    """
    This extension element uses GraphViz to generate a diagram of a given
    resource, a set of resources in a particular scope, or of the entire
    system model. It adds an HTML imagemap (a chunk of text that can be
    placed in a MAP element) to the result tree, and has the side effect
    of generating in the repository an SVG or JPEG image resource
    representing the corresponding diagram.

    output-path specifies the repo path of the diagram to generate. If the
    resource exists already, its content will be overwritten.

    If provided, resourcePath must to refer to either a specific resource in
    the system model or an RDF document in the repository. If it refers to an
    RDF document, then all RDF statements with that document's URI as their
    scope will be graphed. If the resourcePath refers to any other kind of
    resource, then only statements about it will be graphed. If no
    resourcePath is specified, then the entire system model will be graphed.

    The image map text placed in the result tree uses the uriFormat string
    to generate (by appying the uris of all resources against this string)
    the links for all resources in the image. imagemap information is
    generated and returned only if the uriFormat string is specified.

    Most RDF graphs are width heavy and the visualizer can be told to rotate
    the image 90 degrees which can be helpful far larger graphs (which could
    great widths).  This is specified by the rotate attribute. If present,
    the graph is rotated, otherwise it isn't.

    The max-arcs attribute specifies the maximum number of arcs. If the graph
    requires more arcs, then nothing will be generated at all.

    This element can also take a versa query result as its child nodes. In
    this case, all the resources in the result are graphed and resource-path
    is ignored.
    """

    content = ContentInfo.Template
    legalAttrs = {
        'graph-vis' : AttributeInfo.UriReferenceAvt(
            required=1, description=("The directory in which 'graphViz'"
                                     " executable is located.")),
        'output-path' : AttributeInfo.UriReferenceAvt(
            required=1, description=('The name of the diagram resource to be'
                                     ' created')),
        'svg' : AttributeInfo.YesNoAvt(
            default='no', description='generate an svg diagram?'),
        'scoped' : AttributeInfo.YesNoAvt(
            default="no", description=('Whether to render RDF documents as'
                                       'scoped mini-graphs')),
        'map-name' : AttributeInfo.IdAvt(
            description='The ID to use for the image-map'),
        'uri-format' : AttributeInfo.StringAvt(
            description=('A format string to use for the image-map links.'
                         ' Every uri in the graph is applied against this'
                         ' string.')),
        'max-arcs' : AttributeInfo.NumberAvt(
            default=300, description='The maximum number of arcs to allow'),
        'rotate' : AttributeInfo.YesNoAvt(
            default='no', description=('Whether to rotate the graph image'
                                       ' by 90 degrees.')),
        'namespaces' : AttributeInfo.StringAvt(
            default='/ftss/dashboard/NsMappings.xml',
            description=('The location in repository of a namespace mapping'
                         ' document to use for retrieving additional'
                         ' namespaces to truncate predicate names with')),
        'resourcePath' : AttributeInfo.StringAvt(
            description='A resource in the repository to graph'),
        }

    def instantiate(self, context, processor):
        context.setProcessState(self)

        repo = context.processor._repository
        model = repo.getModel()

        graphViz = self._graph_vis.evaluate(context)
        svg = self._svg.evaluate(context)

        try:
            resourcePath = self._resourcePath.evaluate(context)
        except:
            resourcePath = None

        namespaces = self._namespaces.evaluate(context)
        if not strip(graphViz):
            raise ValueError("graphViz executable path not given: ", graphViz)
        outputPath = self._output_path.evaluate(context)

        scpd = self._scoped.evaluate(context)
        print "scpd: ", scpd

        mapName = self._map_name.evaluate(context)
        uriFormat = self._uri_format.evaluate(context)
        rotate = self._rotate.evaluate(context)
        maxArcs = self._max_arcs.evaluate(context)

        from Ft.Rdf import Triclops
        engine = Triclops.RDFGraphVizEngine(graphViz, mapName,
                                            uriFormat, maxArcs)
        nsMappings = repo.fetchResource('/ftss/dashboard/NsMappings.xml')
        newMapDict = ParseNsMappings(nsMappings.getContent(), self.namespaces)
        if self.children:
            # graph from versa results
            processor.pushResultTree(self.baseUri)
            self.processChildren(context, processor)
            node = processor.popResult()
            mapInfo = engine.resourceViewer(model, scoped=scpd,
                                            rotate=rotate, nsDict=newMapDict,
                                            resultsNode=node)

        elif resourcePath:
            mapInfo = engine.resourceViewer(model,
                                            scoped=scpd,
                                            rotate=rotate,
                                            resourceUri=resourcePath,
                                            nsDict=newMapDict
                                            )

        if svg:
            fsvg = open(engine.outputSvg)
            svgcontent = fsvg.read()
            print "creating a graphviz svg diagram file of size: ", len(svgcontent)
            svgPath = outputPath[:outputPath.find('.')] + '.svg'
            if repo.hasResource(svgPath):
                repo.fetchResource(svgPath).setContent(svgcontent)
            else:
                repo.createRawFile(svgPath, 'image/svg-xml', svgcontent)
            fsvg.close()

        else:
            fd = open(engine.outputJpeg)
            content = fd.read()
            print "creating a graphviz image file of size: ", len(content)
            if repo.hasResource(outputPath):
                repo.fetchResource(outputPath).setContent(content)
            else:
                repo.createRawFile(outputPath, 'image/jpeg', content)
            fd.close()

        if uriFormat:
            if svg:
                writer = processor.writers[-1]
                writer.startElement(u'embed')
                src = u'%s?time=%s'%(outputPath, FtTime.FromPythonTime())
                writer.attribute(u'src', src)
                writer.attribute(u'width', unicode(engine.svgWidth))
                writer.attribute(u'height', unicode(engine.svgHeight))
                writer.endElement(u'embed')
            else:
                processor.writers[-1].text(mapInfo, escapeOutput=0)

        return


ExtFunctions = {
    (RDF_NS, 'any'): RdfAny,
    (RDF_NS, 'complete'): RdfComplete,
    (RDF_NS, 'contains'): RdfContains,
    (RDF_NS, 'versa-query'): RdfVersaQueryFunction,
}

ExtElements = {
    (RDF_NS, 'remove'): RdfRemoveElement,
    (RDF_NS, 'complete'): RdfCompleteElement,
    (RDF_NS, 'add'): RdfAddElement,
    (RDF_NS, 'versa-query'): RdfVersaQueryElement,
    (RDF_NS, 'versa-assign'): RdfVersaAssignElement,
    (RDF_NS, 'visualize'): RdfVisualizeElement,
    (RDF_NS, 'deserialize-and-add'): DeserializeAndAddElement,
    (RDF_NS, 'deserialize-and-remove'): DeserializeAndRemoveElement,
}
