########################################################################
#
# File Name:            Processor.py
#
# Documentation:        http://docs.4suite.org/4ODS/Parsers/Odl/Processor.py.html
#
"""
Implementation of an ODL language processor
WWW: http://4suite.org/4ODS        e-mail: support@4suite.org

Copyright (c) 1999-2001 Fourthought Inc, USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""

import os, sys, string, cStringIO

try:
    import OdlParserc
    OdlParser = OdlParserc
except:
    import OdlParser

from Ft.Ods.Parsers.Odl import PreProcessor, PostProcessor, GenerateRepository

class _GenerateParams:
    def __init__(self, package='', directory=''):
        self.curPackage = package
        self.basePackage = package
        self.baseDir = directory
        self.curDir = directory
        self.outFile = None
        self.repository = None
        self.indent = ''
        self.literalFiles = []

    def copy(self):
        new = self.__class__()
        new.__dict__.update(self.__dict__)
        return new

class Processor:
    def __init__(self, database, package=None, directory=None, includes=None,
                 defines=None,preprocessonly=0):
        self.database = database
        self.package = package or ''
        self.directory = directory or os.getcwd()
        self.includes = includes or []
        self.defines = defines or {}
        self.preprocessonly = preprocessonly

    def runString(self, string):
        """Convienence function for parsing plain text ODL."""
        stream = cStringIO.StringIO(string)
        self.run(stream)
        stream.close()
        return

    def runFile(self, file):
        """Convienence function for parsing plain text ODL."""
        stream = open(file)
        self.run(stream)
        stream.close()
        return

    def run(self, stream):
        output = cStringIO.StringIO()
        processor = PreProcessor.Processor(self.includes, self.defines, output)
        if processor.run(stream):
            # there were errors in processing
            raise SyntaxError('ODL preprocessor error')

        if self.preprocessonly:
            print output.getvalue()
            return

        parser = OdlParser.new()
        parse_tree = parser.parse(output.getvalue())

        from Ft.Ods import Database
        db = Database.Database()
        db.open(self.database)
        tx = db.new()
        tx.begin()
        processor = PostProcessor.Processor(db)
        processor.run(parse_tree)

        # Generate Python stubs
        params = _GenerateParams(self.package, self.directory)
        meta_objects = db.schema().resolve('ODLMetaObjects')
        GenerateRepository.GenerateStubs(params, meta_objects)

        # In case the directory is not on the Python path yet
        sys.path.insert(0, self.directory)

        for (type, file, names) in params.literalFiles:
            (file, ext) = os.path.splitext(file)

            prefix = os.path.commonprefix([self.directory, file])
            path = file[len(prefix):]

            # Convert directory path to python path
            path = string.replace(path, os.sep, '.')
            if path[0] == '.':
                path = path[1:]

            # Make sure it's up-to-date
            module = __import__(path, {}, {}, '*')
            reload(module)

            literal = module
            for name in names:
                literal = getattr(literal, name)

            # Get the actual literal class
            _class = getattr(literal, '__class__', literal)
            tx._adapter.newPythonLiteralClass(tx._db, type, literal._typeId, _class)

        # Remove the forced path
        del sys.path[0]
            
        tx.commit()
        db.close()

        return
