########################################################################
#
# File Name: 	        DefiningScope.py
#
# Documentation:	http://docs.ftsuite.com/4ODS/DefiningScope.py.html
#
"""
Implements the DefiningScope meta-data interface.
WWW: http://4suite.org/4ODS         e-mail: support@4suite.org

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

from Ft.Ods.MetaData import MetaObject
from Ft.Ods.MetaData import Scope, CollectionKind
from Ft.Ods.StorageManager.Adapters import Constants
from Ft.Ods.PersistentObject import TupleDefinitions
from Ft.Ods import Exception

class DefiningScope:

    InvalidType = Exception.InvalidType
    InvalidExpression = Exception.InvalidExpression
    CannotRemove = Exception.CannotRemove

    def __init__(self):
        pass
    
    #Override the Scope interface
    def bind(self,name,obj):
        for d in self.defines:
            if d.name == name:
                raise Exception.DuplicateName(name)
        if obj.name != name:
            obj.name = name
        self.add_defines(obj)

    def resolve(self,name):
        if name[0] == ':':
            from Ft.Ods import Database
            return self._db.schema().resolve(name[2:])
        path = string.split(name,'::')
        for d in self.defines:
            if d.name == path[0]:
                break
        else:
            raise Exception.NameNotFound(reason="No attribute %s on %s" % (path[0],str(self)))
        if len(path) == 1:
            return d
        return d.resolve(string.join(path[1:],'::'))

    def unbind(self,name):
        path = string.split(name,'::')
        for d in self.defines:
            if d.name == path[0]:
                break
        else:
            raise Exception.NameNotFound(reason="No attribute %s" % path[0])
        if len(path) == 1:
            self.remove_defines(d)
            return
        return d.unbind(string.join(path[1:],'::'))

    def children(self):
        return self.defines


    def add_defines(self, target, inverse=1):
        self._4ods_addRelationship('defines',MetaObject.MetaObject,'definedIn','form',target,inverse)
    form_defines = add_defines

    def remove_defines(self, target, inverse=1):
        self._4ods_removeRelationship('defines','definedIn','drop',target,inverse)
    drop_defines = remove_defines

    def create_primitive_type(self,kind):
        from Ft.Ods.MetaData import PrimitiveType
        return PrimitiveType.PrimitiveType(self._db,None,pk=kind)
        

    def create_collection(self,kind,max_size,sub_type):
        from Ft.Ods.MetaData import Collection
        c = Collection.Collection(self._db,None,collection_kind = kind)
        if max_size:
            c.form_max_size(max_size)
        if sub_type:
            c.form_subtype(sub_type)
        return c

    def create_dictionary_type(self,key_type,sub_type):
        from Ft.Ods.MetaData import Dictionary
        dict = Dictionary.Dictionary(self._db,None)
        dict.form_subtype(sub_type)
        dict.form_key_type(key_type)
        return dict

    def create_operand(self, expression):
        from Ft.Ods.Parsers.Odl import OdlParser, PostProcessor

        parser = OdlParser.new()
        parse_tree = parser.parse('!' + expression)

        return PostProcessor.ProcessConstExp(self._db,parse_tree[1],self)


    def create_member(self, memberName, memberType):
        from Ft.Ods.MetaData import Member
        m = Member.Member(self._db,None)
        m.name = memberName
        m.form_type(memberType)
        return m

    def create_union_case(self,case_name,case_type,case_labels):
        from Ft.Ods.MetaData import UnionCase
        uc = UnionCase.UnionCase(self._db,None)
        uc.name = case_name
        uc.form_type(case_type)
        for cl in case_labels:
            uc.add_case_labels(cl)
        return uc


    def add_constant(self,name,typ,value):
        from Ft.Ods.MetaData import Constant
        c = Constant.Constant(self._db,None)
        c.form_type(typ)
        c.name = name
        c.form_the_Value(value)
        self.add_defines(c)
        return c

    def add_type_definition(self,name,alias):
        from Ft.Ods.MetaData import TypeDefinition
        t = TypeDefinition.TypeDefinition(self._db,None)
        t.name = name
        t.form_alias(alias)
        self.add_defines(t)
        return t

    def add_enumeration(self,name,element_names):
        from Ft.Ods.MetaData import Enumeration
        from Ft.Ods.MetaData import Constant
        from Ft.Ods.MetaData import Literal

        #NOTE WE don't use add_constant because the values of an
        #enum should not be defined at self's level

        e = Enumeration.Enumeration(self._db,None)
        e.name = name
        ctr = 0

        t = self.resolve('::pk_short')
        for n in element_names:
            l = Literal.Literal(self._db,None)
            l.literal_value = ctr
            c = Constant.Constant(self._db,None)
            c.name = n
            c.form_type(t)
            c.form_the_Value(l)
            e.bind(n,c)
            ctr = ctr + 1
        self.add_defines(e)
        return e

    def add_structure(self,name,fields):
        from Ft.Ods.MetaData import Structure
        s = Structure.Structure(self._db,None)
        s.name = name
        for f in fields:
            s.bind(f.name,f)
        self.add_defines(s)
        return s

    def add_union(self,name,switch_type,cases):
        from Ft.Ods.MetaData import Union
        u = Union.Union(self._db,None)
        u.name = name
        u.form_switch_type(switch_type)
        for c in cases:
            u.bind(c.name,c)
        self.add_defines(u)
        return u

    def add_exception(self,name,result):
        from Ft.Ods.MetaData import Exception
        e = Exception.Exception(self._db,None)
        e.name = name
        e.form_result(result)
        self.add_defines(e)
        return e

    def remove_constant(self,c):
        if not c._4ods_checkRemove():
            raise DefiningScope.CannotRemove(reason='Failed Remove Check')
        self.remove_defines(c)
        c.delete()
        
    def remove_type_definition(self,t):
        if not t._4ods_checkRemove():
            raise DefiningScope.CannotRemove(reason='Failed Remove Check')
        self.remove_defines(t)
        t.delete()

    def remove_enumeration(self,e):
        if not e._4ods_checkRemove():
            raise DefiningScope.CannotRemove(reason='Failed Remove Check')
        for el in e.elements:
            el.drop_type(el.type)
            tv = el.the_Value
            el.drop_the_Value(tv)
            tv.delete()
            el.delete()
        self.remove_defines(e)
        e.delete()

    def remove_structure(self,s):
        if not s._4ods_checkRemove():
            raise DefiningScope.CannotRemove(reason='Failed Remove Check')
        self.remove_defines(s)
        s.delete()
        
    def remove_union(self,u):
        if not u._4ods_checkRemove():
            raise DefiningScope.CannotRemove(reason='Failed Remove Check')
        u.drop_switch_type(u.switch_type)
        for c in u.cases:
            u.remove_cases(c)
        self.remove_defines(u)
        u.delete()

    def remove_exception(self,e):
        if not e._4ods_checkRemove():
            raise DefiningScope.CannotRemove(reason='Failed Remove Check')
        e.drop_result(e.result)
        self.remove_defines(e)
        e.delete()

    _tupleNames = (('defines',),)
    _tupleDefinitions = {'defines':{TupleDefinitions.TYPE:Constants.Types.LIST_COLLECTION,
                                    TupleDefinitions.READONLY:1,
                                    TupleDefinitions.RELATIONSHIP:1,
                                    TupleDefinitions.COLLECTION_SUBTYPE:Constants.Types.ROBJECT,
                                    },
                         }


    _localExtents = ()
