########################################################################
# $Header: /var/local/cvsroot/4Suite/Ft/Rdf/_4versa.py,v 1.27 2003/11/11 03:01:21 mbrown Exp $
"""
Versa command-line application

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

import os, sys, getopt, cStringIO
from Ft.Rdf import __version__
from Ft.Rdf import RDF_SCHEMA_BASE, Util
from Ft.Rdf.Parsers import Versa
from Ft.Rdf.Parsers.Versa import DataTypes, VERSA_NS

__doc__ = """\
4Versa command-line query application
Note: this is a general-purpose Versa tool. If you wish to query a 4Suite
repository, you may find '4ss rdf versa' more convenient."""

INVOKED_AS = sys.argv[0]
from Ft.Xml import Domlette
READER = Domlette.NonvalidatingReader

BASE_NS_MAP = {'versa': VERSA_NS,
               'vsort': VERSA_NS + 'sort/',
               'vtrav': VERSA_NS + 'traverse/',
               'daml': 'http://www.daml.org/2001/03/daml+oil#',
               'dc': 'http://purl.org/dc/elements/1.1/',
               }


def Run(options, args):
    driver = options.get('driver')
    dbname = options.get('dbname')
    modelName = options.get('modelname','default')
    scope = options.get('scope')
    rdf_files = options.get('rdf-file')
    if not isinstance(rdf_files, type([])):
        if rdf_files:
            rdf_files = [ rdf_files ]
        else:
            rdf_files = [ ]
    srcFile = options.get('qfile')
    in_format = options.get('input-format', 'rdfxml')

    if not rdf_files:
        if not driver:
            raise SystemExit("No driver specified.  Use %s -h to get help"%INVOKED_AS)
        if not dbname:
            raise SystemExit("No datbase name specified.  Use %s -h to get help"%INVOKED_AS)
    else:
        if not driver: driver = "Memory"
        if not dbname: dbname = ""

    try:
        driver = "Ft.Rdf.Drivers." + driver
        driver_mod = __import__(driver, {}, {}, ["*"])
    except:
        raise SystemExit("Unknown driver '%s'" % driver)

    nsMap = BASE_NS_MAP
    if rdf_files:
        if in_format == "rdfxml":
            for f in rdf_files:
                doc = READER.parseUri(f)
                model, db = Util.DeserializeFromNode(doc, driver=driver_mod,
                                                     dbName=dbname, scope=f)
                nsMap.update(Domlette.GetAllNs(doc.documentElement))
        elif in_format == "ntriples":
            from Ft.Rdf.Serializers.NTriples import Serializer
            from Ft.Rdf import Model, RDF_MS_BASE, RDF_SCHEMA_BASE, DAML_OIL_NS
            from Ft.Rdf.Drivers import Memory
            serializer = Serializer()
            db = Memory.CreateDb("")
            model = Model.Model(db)
            for f in rdf_files:
                serializer.deserialize(model, open(f, "r"))
    else:
        if driver_mod.ExistsDb(dbname, modelName):
            db = driver_mod.GetDb(dbname, modelName)
        else:
            raise SystemExit("Unknown Database '%s'" % dbname)
        model = None
    db.begin()

    if srcFile:
        if model:
            con = Versa.CreateContext(model=model, nsMapping=nsMap, scope=scope)
        else:
            con = Versa.CreateContext(driver=db, nsMapping=nsMap, scope=scope)
        import Ft.Rdf.Parsers.Versa.Util
        result = Ft.Rdf.Parsers.Versa.Util.ProcessQueryFile(
            open(srcFile), con, sys.stderr
            )
    else:
        query = args.get('query')
        if not query:
            sys.stderr.write("Enter Query: ")
            query = sys.stdin.readline()
            sys.stderr.write("\n")
            #raise SystemExit("You must either specify a query on the command line our use the --file option")
        query = query[0]
        if nsMap:
            sys.stderr.write("With nsMapping of:\n")
            for name, value in nsMap.items():
                sys.stderr.write("%s --> %s\n"%(name, value))
        if model:
            con = Versa.CreateContext(model=model, nsMapping=nsMap, scope=scope)
        else:
            con = Versa.CreateContext(driver=db, nsMapping=nsMap, scope=scope)

        exp = Versa.Compile(query)
        result = exp.evaluate(con)

    db.rollback()
    ResultsToXml(result, sys.stdout)
    return


def ResultsToXml(result, stream, indent=''):
    # By giving a namespace, the elements are created in the null-namespace
    if DataTypes.IsList(result):
        stream.write(indent)
        stream.write("<List>\n")
        for r in result:
            ResultsToXml(r, stream, indent+'  ')
        stream.write(indent)
        stream.write("</List>\n")
    elif DataTypes.IsSet(result):
        stream.write(indent)
        stream.write("<Set>\n")
        for r in DataTypes.ToList(result):
            ResultsToXml(r, stream, indent+'  ')
        stream.write(indent)
        stream.write("</Set>\n")
    elif DataTypes.IsString(result):
        stream.write(indent)
        stream.write("<String>%s</String>\n"%result)
    elif DataTypes.IsResource(result):
        stream.write(indent)
        stream.write("<Resource>%s</Resource>\n"%result)
    elif DataTypes.IsNumber(result):
        stream.write(indent)
        stream.write("<Number>%s</Number>\n"%result)
    elif DataTypes.IsBoolean(result):
        stream.write(indent)
        stream.write("<Boolean>%s</Boolean>\n"%result)
    else:
        raise Exception('Unknown Versa result type')
    return


from Ft.Rdf.Parsers.Versa import DataTypes
INDENT = '  '
def PrettyPrintResults(results, indent=0):
    if DataTypes.IsList(results):
        print INDENT*indent + "List:"
        for r in results:
            PrettyPrintResults(r, indent+1)
    elif DataTypes.IsSet(results):
        print INDENT*indent + "Set:"
        for r in DataTypes.ToList(results):
            PrettyPrintResults(r, indent+1)
    else:
        print INDENT*indent + str(results)


from Ft.Lib.CommandLine import Options, CommandLineApp, Arguments, Command

class VersaCommandLineApp(CommandLineApp.CommandLineApp):

    def __init__(self):
        CommandLineApp.CommandLineApp.__init__(
            self,
            '4versa',
            '4RDF Query version %s' % __version__,
            __doc__,
            [],
            ourOptions = Options.Options([Options.Option(None,
                                                         'driver=DRIVER',
                                                         'Use the given 4RDF backend driver',
                                                         ),
                                          Options.Option(None,
                                                         'dbname=DBNAME',
                                                         'Query the RDF model database '\
                                                         'with the given name',
                                                         ),
                                          Options.Option(None,
                                                         'modelname=MODELNAME',
                                                         'the name of the model in the database',
                                                         ),
                                          Options.Option(None,
                                                         'scope=SCOPE',
                                                         'a scope to restrict the query',
                                                         ),
                                          Options.Option('r',
                                                         'rdf-file=URI',
                                                         'URI of an RDF file to use as the model',
                                                         ),
                                          Options.Option(None,
                                                         'input-format=FORMAT',
                                                         'The format of the input file, can be "rdfxml" (the default) or "ntriples"'),
                                          Options.Option('q',
                                                         'qfile=URI',
                                                         'File to read query from. It must have a' \
                                                         ' line that reads QUERY=<query>.'\
                                                         ' where <query> is the query to execute.'\
                                                         ' It can also have multiple lines of'\
                                                         ' NS=<prefix> <URI> where <prefix>'\
                                                         ' is a namespace prefix and <URI>'\
                                                         ' is a URI to map it to',
                                                         ),
                                          ]),

            enableShowCommands = 0
            )

        self.function = Run
        self.arguments = [
            Arguments.ZeroOrMoreArgument('query',
                                        'The query to perform. Required if --file is not specified',
                                        str),
            ]

    def validate_arguments(self, args):
        return Command.Command.validate_arguments(self,args)


