#####################################################################
#####################################################################
## Versavant Topic Map Application Bus / Subject Addressing Engine ##
## Copyright 2005 Steven R. Newcomb                                ##
##                                                                 ##
## This software is licensed under the Apache License, Version 2.0 ##
## (the "License"); you may not use this file except in compliance ##
## with the License.  Copies of the License are available at       ##
##        http://www.apache.org/licenses/LICENSE-2.0               ##
##                                                                 ##
## Unless required by applicable law or agreed to in writing,      ##
## software distributed under the License is distributed on an "AS ##
## IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either ##
## express or implied.  See the License for the specific language  ##
## governing permissions and limitations under the License.        ##
##                                                                 ##
## Copies of the source code and the license are available at      ##
##        http://www.versavant.org                                 ##
#####################################################################
#####################################################################

####################################
cvsRevision = "$Revision: 1.3 $" ##
####################################

import types

#################################################
## implementation of                           ##
## stringKeyDictOfStringKeyDictsWithStringListValues                         ##
## (for all U_assert property classes)
#################################################

#######################################################
def ValueIsNotOfType_stringKeyDictOfStringKeyDictsWithStringListValues_( vsv, propertyValue, propertyClass, propertyInstance = None, **args):

    topicMap = propertyClass.topicMap
    levelAtWhichProxyReferenceAppears = args[ 'levelAtWhichProxyReferenceAppears'] ## 'first', 'second', or 'third'

    if not isinstance( propertyValue, types.DictType):
        if propertyInstance:
            vsv.errMsg( 'Value of property %s ("%s") is not a dictionary.' % ( propertyInstance.propertyInstanceKey, propertyValue))
        else:
            vsv.errMsg( 'Value of presumptive %s property ("%s") is not a dictionary.' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), propertyValue))
        return True

    if not len( propertyValue):
        if propertyInstance:
            vsv.errMsg( 'Value of property %s ("%s") is a dictionary with no keys in it.' % ( propertyInstance.propertyInstanceKey, propertyValue))
        else:
            vsv.errMsg( 'Value of presumptive %s property ("%s") is a dictionary with no keys in it.' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), propertyValue))
        return True

    for dict1Key in propertyValue:
        if not isinstance( dict1Key, types.StringType):
            if propertyInstance:
                vsv.errMsg( 'Key "%s" in the dictionary that is the value of property %s is not a string.' % ( dict1Key, propertyInstance.propertyInstanceKey))
            else:
                vsv.errMsg( 'Key "%s" in the dictionary that is the presumptive value of a %s property is not a string.' % ( dict1Key, vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName)))
            return True

        dict2 = propertyValue[ dict1Key]

        if not isinstance( dict2, types.DictType):
            if propertyInstance:
                vsv.errMsg( 'Value of key "%s" in the dictionary that is the value of property %s ("%s") is not a dictionary.' % ( dict1Key, propertyInstance.propertyInstanceKey, dict2))
            else:
                vsv.errMsg( 'Value of key "%s" in the dictionary that is the presumptive value of a %s property ("%s") is not a dictionary.' % ( dict1Key, vsv.catKey( propertyClass.TMA.TMAName, dict2)))
            return True

        if not len( dict2):
            if propertyInstance:
                vsv.errMsg( 'Value of property %s is a dictionary with key "%s" whose value is a dictionary with no keys in it.' % ( propertyInstance.propertyInstanceKey, dict1Key))
            else:
                vsv.errMsg( 'Value of presumptive %s property is a dictionary with key "%s" whose value is a dictionary with no keys in it.' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), dict1Key))
            return True

        if levelAtWhichProxyReferenceAppears == 'first':
            if proxyIdIsBad( vsv, dict1Key, levelAtWhichProxyReferenceAppears, propertyInstance, propertyClass):
                return True
        else:
            if vsv.looksLikeProxyId( dict1Key):
                bool, proxyIdLookalikeREString = vsv.looksLikeProxyId( dict1Key)
                if propertyInstance:
                    vsv.errMsg( 'Value of property %s is a dictionary with a key ("%s") that is bad because it looks like a proxyId.  Role names cannot match the regular expression "%s".' % ( propertyInstance.propertyInstanceKey, dict1Key, proxyIdLookalikeREString))
                else:
                    vsv.errMsg( 'Value of presumptive %s property is a dictionary with a key ("%s") that is bad because it looks like a proxyId.  Role names cannot match the regular expression "%s".' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), dict1Key, proxyIdLookalikeREString))
                return True

        for dict2Key in dict2:
            if not isinstance( dict2Key, types.StringType):
                if propertyInstance:
                    vsv.errMsg( 'Key "%s" in the dictionary that is the value of key "%s" in the dictionary that is the value of property %s is not a string.' % ( dict2Key, dict1Key, propertyInstance.propertyInstanceKey))
                else:
                    vsv.errMsg( 'Key "%s" in the dictionary that is the value of key "%s" in the dictionary that is the presumptive value of a %s property is not a string.' % ( dict2Key, dict1Key, vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName)))
                return True

            if levelAtWhichProxyReferenceAppears == 'second':
                if proxyIdIsBad( vsv, dict2Key, levelAtWhichProxyReferenceAppears, propertyInstance, propertyClass):
                    return True
            else:
                if vsv.looksLikeProxyId( dict2Key):
                    bool, proxyIdLookalikeREString = vsv.looksLikeProxyId( dict2Key)
                    if propertyInstance:
                        vsv.errMsg( 'Value of property %s is a dictionary with a key ("%s") whose value is a dictionary with a key ("%s") that is bad because it looks like a proxyId.  Role names cannot match the regular expression "%s".' % ( propertyInstance.propertyInstanceKey, dict1Key, dict2Key, proxyIdLookalikeREString))
                    else:
                        vsv.errMsg( 'Value of presumptive %s property is a dictionary with a key ("%s") whose value is a dictionary with a key ("%s") that is bad because it looks like a proxyId.  Role names cannot match the regular expression "%s".' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), dict1Key, dict2Key, proxyIdLookalikeREString))
                    return True

            stringList = dict2[ dict2Key]

            if not isinstance( stringList, types.ListType):
                if propertyInstance:
                    vsv.errMsg( 'Value of key "%s" in the dictionary that is the value of key "%s" in the dictionary that is the value of property %s is not a list.' % ( dict2Key, dict1Key, propertyInstance.propertyInstanceKey))
                else:
                    vsv.errMsg( 'Value of key "%s" in the dictionary that is the value of key "%s" in the dictionary that is the presumptive value of a %s property is not a list.' % ( dict2Key, dict1Key, vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName)))
                return True

            if not len( stringList):
                if propertyInstance:
                    vsv.errMsg( 'Value of property %s is a dictionary with key "%s" whose value is a dictionary with key "%s" whose value is a list with no items in it.' % ( propertyInstance.propertyInstanceKey, dict1Key, dict2Key))
                else:
                    vsv.errMsg( 'Value of presumptive %s property is a dictionary with key "%s" whose value is a dictionary with key "%s" whose value is a list with no items in it.' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), dict1Key, dict2Key))
                return True

            if levelAtWhichProxyReferenceAppears == 'third':
                if dict1Key == dict2Key:
                    if propertyInstance:
                        vsv.errMsg( 'Value of property %s specifies a relationship in which two role players play roles which have the same name.  Each role in a relationship must have a different name.  dict1Key == "%s"; dict2Key == "%s".' % ( propertyInstance.propertyInstanceKey, dict1Key, dict2Key))
                    else:
                        vsv.errMsg( 'Value of presumptive %s property specifies a relationship in which two role players play roles which have the same name.  Each role in a relationship must have a different name.  dict1Key == "%s"; dict2Key == "%s".' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), dict1Key, dict2Key))
                    return True

            for string in stringList:
                if not isinstance( string, types.StringType):
                    if propertyInstance:
                        vsv.errMsg( 'Item ("%s") in the list that is the value of key "%s" in the dictionary that is the value of key "%s" in the dictionary that is the value of property %s is not a list.' % ( string, dict2Key, dict1Key, propertyInstance.propertyInstanceKey))
                        return True
                    else:
                        vsv.errMsg( 'Item ("%s") in the list that is the value of key "%s" in the dictionary that is the value of key "%s" in the dictionary that is the presumptive value of a %s property is not a list.' % ( string, dict2Key, dict1Key, vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName)))
                        return True
                if levelAtWhichProxyReferenceAppears == 'third':
                    if proxyIdIsBad( vsv, string, levelAtWhichProxyReferenceAppears, propertyInstance, propertyClass):
                        return True
                else:
                    if levelAtWhichProxyReferenceAppears == 'first':
                        if dict2Key == string:
                            if propertyInstance:
                                vsv.errMsg( 'Value of property %s specifies a relationship in which two role players play roles which have the same name.  Each role in a relationship must have a different name.  dict2Key == "%s"; string == "%s".' % ( propertyInstance.propertyInstanceKey, dict2Key, string))
                            else:
                                vsv.errMsg( 'Value of presumptive %s property specifies a relationship in which two role players play roles which have the same name.  Each role in a relationship must have a different name.  dict2Key == "%s"; string == "%s".' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), dict2Key, string))
                            return True
                            
                    if levelAtWhichProxyReferenceAppears == 'second':
                        if dict1Key == string:
                            if propertyInstance:
                                vsv.errMsg( 'Value of property %s specifies a relationship in which two role players play roles which have the same name.  Each role in a relationship must have a different name.  dict1Key == "%s"; string == "%s".' % ( propertyInstance.propertyInstanceKey, dict1Key, string))
                            else:
                                vsv.errMsg( 'Value of presumptive %s property specifies a relationship in which two role players play roles which have the same name.  Each role in a relationship must have a different name.  dict1Key == "%s"; string == "%s".' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), dict1Key, string))
                            return True
                        
                    if vsv.looksLikeProxyId( string):
                        bool, proxyIdLookalikeREString = vsv.looksLikeProxyId( string)
                        if propertyInstance:
                            vsv.errMsg( 'Value of property %s is a dictionary with a key ("%s") whose value is a dictionary with a key ("%s") whose value is a string list that contains a string ("%s") that is bad because it looks like a proxyId.  Role names cannot match the regular expression "%s".' % ( propertyInstance.propertyInstanceKey, dict1Key, dict2Key, string, proxyIdLookalikeREString))
                        else:
                            vsv.errMsg( 'Value of presumptive %s property is a dictionary with a key ("%s") whose value is a dictionary with a key ("%s") whose value is a string list that contains a string ("%s") that is bad because it looks like a proxyId.  Role names cannot match the regular expression "%s".' % ( vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), dict1Key, dict2Key, string, proxyIdLookalikeREString))
                        return True
            
    return False

#######################################################
def InstanceOf_stringKeyDictOfStringKeyDictsWithStringListValues_ValueTypeToXMLContentString( vsv, propertyInstance):
    return vsv.cPickleDumpObjectAsXMLContentString( propertyInstance._value)

#######################################################
def XMLContentStringToInstanceOf_stringKeyDictOfStringKeyDictsWithStringListValues_ValueType( vsv, string, propertyClass, **args):
    ## Import a stringKeyDictOfStringKeyDictsWithStringListValues, adjusting the proxyIds as necessary to
    ## the new proxyIds of the imported proxies to which they refer.

    propertyClassName = propertyClass.propertyClassName
    TMAName = propertyClass.TMA.TMAName

    levelAtWhichProxyReferenceAppears = args[ 'levelAtWhichProxyReferenceAppears'] ## 'first', 'second', or 'third'

    oldDict1 = vsv.cPickleLoadObjectFromXMLContentString( string)

    mem2xmlProxyIdDict = vsv.currentTopicMapHoldingDict[ 'IoMapsDict'][ 'ProxyIds.mem2xml']
        ## This dictionary allows us to look up the in-xml element id of a
        ## proxy, given the proxy's proxyId as it was in the topic map
        ## from which the proxy was exported as xml.

    firstImportedProxyId = vsv.firstImportedProxyId
        ## This is an integer which, if we were to convert it into a
        ## string of decimal digits with 2 prepended zero characters,
        ## would be the proxyId of the first (the first, that is, not
        ## counting the proxies created in order to register the TMAs
        ## of the xml topic map being imported) in-memory proxy to be
        ## imported into the being-imported-into topic map from the
        ## xml topic map that we're importing.


    newDict1 = {}

    for oldDict1Key in oldDict1:
        oldDict2 = oldDict1[ oldDict1Key]
        
        if levelAtWhichProxyReferenceAppears == 'first':
            ## key is a proxyId that needs to be replaced
            
            newDict1Key = newProxyIdForOld( vsv, oldDict1Key, mem2xmlProxyIdDict, firstImportedProxyId, propertyClassName, TMAName)
                ## convert the old proxyId (the one it had in the XML file
                ## being imported) into its new proxyId in the topic map into
                ## which it's being imported.

        else:
            ## key is not a proxyId
            newDict1Key = oldDict1Key

        if newDict1.has_key( newDict1Key):
            import traceback
            traceback.print_stack()
            vsv.errMsg( 'This should never happen.  newDict1Key == %s' % ( newDict1Key))
            sys.exit( 1)

        newDict2 = newDict1[ newDict1Key] = {}

        for oldDict2Key in oldDict2:
            oldList3 = oldDict2[ oldDict2Key]
            
            if levelAtWhichProxyReferenceAppears == 'second':
                ## key is a proxyId that needs to be replaced

                newDict2Key = newProxyIdForOld( vsv, oldDict2Key, mem2xmlProxyIdDict, firstImportedProxyId, propertyClassName, TMAName)
                    ## convert the old proxyId (the one it had in the XML file
                    ## being imported) into its new proxyId in the topic map into
                    ## which it's being imported.

            else:
                ## key is not a proxyId
                newDict2Key = oldDict2Key

            if newDict1[ newDict1Key].has_key( newDict2Key):
                import traceback
                traceback.print_stack()
                vsv.errMsg( 'This should never happen.  newDict2Key == %s' % ( newDict2Key))
                sys.exit( 1)

            newList3 = newDict1[ newDict1Key][ newDict2Key] = []

            for oldListItem in oldList3:

                if levelAtWhichProxyReferenceAppears == 'third':
                    ## list item is a proxyId that needs to be replaced

                    newListItem = newProxyIdForOld( vsv, oldListItem, mem2xmlProxyIdDict, firstImportedProxyId, propertyClassName, TMAName)
                        ## convert the old proxyId (the one it had in the XML file
                        ## being imported) into its new proxyId in the topic map into
                        ## which it's being imported.

                else:
                    ## list item is not a proxyId
                    newListItem = oldListItem

                if newListItem in newList3:
                    import traceback
                    traceback.print_stack()
                    vsv.errMsg( 'This should never happen.  newListItem == %s' % ( newListItem))
                    sys.exit( 1)

                newList3.append( newListItem)



    ## vsv.currentTopicMapHoldingDict[ 'IoMapsDict'][ 'TMAs'][ args[ 'TMAName']][ propertyClassName]
    ## will be consulted by the ValueIsNotOfType__() function in order
    ## to avoid a logical logjam over this.  The logical logjam is
    ## that we're referring to proxies that haven't been imported yet,
    ## and the ValueIsNotOfType__() function checks to see that the
    ## proxy references are valid.  This gives the
    ## ValueIsNotOfType__() function a basis on which to give a free
    ## pass to proxy references that aren't yet valid.  (See
    ## proxyIdIsBad(), below.)

    return newDict1


#######################################################
def newProxyIdForOld( vsv, proxyIdAsImported, mem2xmlProxyIdDict, firstImportedProxyId, propertyClassName, TMAName):
    ## Helper function for XMLContentStringToInstanceOf_stringKeyDictOfStringKeyDictsWithStringListValues_ValueType()
    ## convert the old proxyId (the one it had in the XML file
    ## being imported) into its new proxyId in the topic map into
    ## which it's being imported.

    xmlProxyNum = mem2xmlProxyIdDict[ int( proxyIdAsImported)]
    newProxyNum = (xmlProxyNum + firstImportedProxyId) - 1
    newProxyId = '00%s' % ( newProxyNum)

    appDict = vsv.currentTopicMapHoldingDict[ 'IoMapsDict'][ 'TMAs'][ TMAName]
    if not appDict.has_key( propertyClassName):
        appDict[ propertyClassName] = [ newProxyId]
    else:
        if newProxyId not in appDict[ propertyClassName]:
            appDict[ propertyClassName].append( newProxyId)
    return newProxyId


#######################################################
def proxyIdIsBad( vsv, proxyId, levelAtWhichProxyReferenceAppears, propertyInstance, propertyClass):
    ## helper function for ValueIsNotOfType_stringKeyDictOfStringKeyDictsWithStringListValues_()

    topicMap = propertyClass.topicMap

    if levelAtWhichProxyReferenceAppears == 'first' or levelAtWhichProxyReferenceAppears == 'second':
        levelIdentifyingString = '%s-level dictionary' % ( levelAtWhichProxyReferenceAppears)
    elif levelAtWhichProxyReferenceAppears == 'third':
        levelIdentifyingString = '%s-level list of strings' % ( levelAtWhichProxyReferenceAppears)
    else:
        vsv.errMsg( 'Unrecognized levelAtWhichProxyReferenceAppears ("%s")' % ( levelAtWhichProxyReferenceAppears))
        return True

    if not vsv.proxyIdStrValidatorRE.match( proxyId):
        if propertyInstance:
            vsv.errMsg( 'Key "%s" in the %s that is the value of property %s is not a proxy identifier string (a proxyId).' % ( proxyId, levelIdentifyingString, propertyInstance.propertyInstanceKey))
        else:
            vsv.errMsg( 'Key "%s" in the %s that is the presumptive value of a %s property is not a proxy identifier string (a proxyId).' % ( proxyId, levelIdentifyingString, vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName)))
        return True

    proxy = vsv.isProxyId( proxyId, topicMap)
    if not proxy:
        if proxyId not in vsv.currentTopicMapHoldingDict[ 'IoMapsDict'][ 'TMAs'][ propertyInstance.TMAName][ propertyClass.propertyClassName]:
               ## We're probably in the process of importing a topic map, and these are
               ## references to proxies that don't exist yet because they haven't been imported
               ## yet.  The above check is supposed to calm our fears that this proxy reference
               ## is invalid, giving us reason to believe that it will soon be valid, even
               ## though it's not quite valid yet.

            if propertyInstance:
                vsv.errMsg( 'proxyId "%s" in the %s that is the value of property %s is not the ID of any proxy in topic map "%s".' % ( proxyId, levelIdentifyingString, propertyInstance.propertyInstanceKey, propertyInstance.topicMap.topicMapName))
            else:
                vsv.errMsg( 'proxyId "%s" in the %s that is the presumptive value of a %s property is not the ID of any proxy in topic map "%s".' % ( proxyId, levelIdentifyingString, vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName), topicMap.topicMapName))
            return True
    if proxy:
        if proxy.forwardTo:
            if propertyInstance:
                vsv.errMsg( 'proxyId "%s" in the %s that is the value of property %s is the ID of a dead proxy.  Only the IDs of living proxies are permitted.' % ( proxyId, levelIdentifyingString, propertyInstance.propertyInstanceKey))
            else:
                vsv.errMsg( 'proxyId "%s" in the %s that is the value of property %s is the ID of a dead proxy.  Only the IDs of living proxies are permitted.' % ( proxyId, levelIdentifyingString, vsv.catKey( propertyClass.TMA.TMAName, propertyClass.propertyClassName)))
            return True
        
    return False
