#! /usr/bin/env python
# ______________________________________________________________________
"""Module ANOITrie.py
Jonathan Riehl
$Id: ANOITrie.py,v 1.11 2005/01/21 00:28:05 jriehl Exp $
"""
# ______________________________________________________________________
# Module imports
import types
import ANOIBaseSpace
import ANOITypes
# ______________________________________________________________________
# Module data
__DEBUG__ = False
if __DEBUG__:
import ANOIUtil
# ______________________________________________________________________
# Class definitions
class ANOITrieError (ANOIBaseSpace.ANOIException):
pass
# ______________________________________________________________________
class ANOISimpleTrie (object):
"""Class ANOISimpleTrie
Basically a string dictionary. The namespace is encoded as a trie,
hanging off the root UID. Namespace lookup is handled by converting
a string into a vector of UID's, which are then crossed with each other.
The root UID doubles as a reference pointer to the contained value, (this
allows substrings of keys to map to values as well).
getName('abcd') = (((((ROOT x a) x b) x c) x d) x REF)
setName('abc', 1011) => ((((ROOT x a) x b) x c) x REF) = 1011
(Where REF = ROOT for ANOISimpleTrie)
This was created with the intention of being the preferred way to encode
properties in a space (as demonstrated in ANOITrie).
"""
# ____________________________________________________________
def __init__ (self, space, root):
"""ANOISimpleTrie.__init__()
"""
self.space = space
self.root = root
self.ref = root
# ____________________________________________________________
def getVectors (self, crntUid = None, crntVec = None, visited = None,
result = None):
"""ANOISimpleTrie.getVectors()
Return a dictionary of vectors in the trie. This should be
overloaded by child classes since this implementation will chase type
and parent links.
"""
if __DEBUG__:
print "ANOISimpleTrie.getVectors():", crntUid, crntVec
if crntUid == None:
crntUid = self.root
if crntVec == None:
crntVec = [crntUid]
if visited == None:
visited = []
if result == None:
result = {}
assert crntUid not in visited, ("You should know better than "
"to run this method on a trie"
" that has circular edges!")
visited.append(crntUid)
keyUidList = self.space.getUidKeys(crntUid)
for keyUid in keyUidList:
if keyUid == self.ref:
referencedUid = self.space.cross(crntUid, keyUid)
if referencedUid != ANOIBaseSpace.NIL:
result[tuple(crntVec[1:])] = referencedUid
else:
nextUid = self.space.cross(crntUid, keyUid)
crntVec.append(keyUid)
self.getVectors(nextUid, crntVec, visited, result)
del crntVec[-1]
return result
# ____________________________________________________________
def getVector (self, vec):
"""ANOISimpleTrie.getVector()
"""
vec = vec[:]
vec.insert(0, self.root)
vec.append(self.ref)
return self.space.getVector(vec)
# ____________________________________________________________
def setVector (self, vec, uid):
"""ANOISimpleTrie.setVector()
"""
vec = vec[:]
vec.insert(0, self.root)
vec.append(self.ref)
return self.space.setVector(vec, uid, self.createTrieNode)
# ____________________________________________________________
def hasName (self, name):
"""ANOISimpleTrie.hasName()
"""
return self.getName(name) != ANOIBaseSpace.NIL
# ____________________________________________________________
def getName (self, name):
"""ANOISimpleTrie.getName()
"""
retVal = self.getVector(map(ord, list(name)))
if __DEBUG__:
print self, "ANOISimpleTrie.getName('%s') = %d" % (name, retVal)
return retVal
# ____________________________________________________________
def setName (self, name, uid):
"""ANOISimpleTrie.setName()
"""
if __DEBUG__:
print self, "ANOISimpleTrie.setName('%s', %d)" % (name, uid)
return self.setVector(map(ord, list(name)), uid)
# ____________________________________________________________
def createTrieNode (self, space, prevUid, keyUid):
"""ANOISimpleTrie.createTrieNode()
"""
retVal = space.getUid()
space.crossEquals(prevUid, keyUid, retVal)
if __DEBUG__:
print "ANOISimpleTrie.createTrieNode():", prevUid, "x", keyUid,
print "=", retVal
return retVal
# ______________________________________________________________________
class ANOITrie (ANOISimpleTrie):
"""Class ANOITrie
"""
# ____________________________________________________________
def __init__ (self, space, root, baseTrie = None):
"""ANOITrie.__init__()
So there is some circular foo going on here. ANOITrie will need to
register with the type system, but the type system will need a trie
to set itself up. So the first step is to get provisional property
keys, then initialize the type system, then register the provisional
property keys with the type system.
"""
assert type(root) == types.IntType
self.space = space
self.root = root
if baseTrie == None:
self.baseTrie = self
# See if we need a base trie to get going.
self.ref = root
refUid = self.getName("REF")
if refUid != ANOIBaseSpace.NIL:
# A bootstrap REF was found, ignore the baseTrie
self.ref = refUid
# Ensure trie was properly bootstrapped.
assert self.getName("REF") == self.ref
self.typeUid = self.getName("ANOITrieType")
self.parent = self.getName("PARENT")
else:
# We need to bootstrap; create a simple trie, initialize
# a type system internal to the simple trie, then copy
# it.
bootTrieUid = self.space.getUid()
bootTrieObj = ANOISimpleTrie(self.space, bootTrieUid)
bootTypeSystem = ANOITypes.ANOITypeManager(space, bootTrieObj)
self.ref = bootTypeSystem.createPropertyKey("REF")
self.parent = bootTypeSystem.createPropertyKey("PARENT")
slots = {self.ref : bootTypeSystem.atomType,
self.parent : None}
self.typeUid = bootTypeSystem.createType("ANOITrieType",
slots)
self.typeSystem = bootTypeSystem
self.copySimpleTrie(bootTrieObj)
refVec = map(ord, list("REF"))
refVec.insert(0, self.root)
refVec.append(self.root) # Bootstrap REF
if __DEBUG__:
print "ANOITrie.__init__(): refVec =", refVec
self.space.setVector(refVec, self.ref)
else:
self.baseTrie = baseTrie
self.typeUid = baseTrie.getName("ANOITrieType")
self.ref = baseTrie.getName("REF")
self.parent = baseTrie.getName("PARENT")
self.typeSystem = ANOITypes.ANOITypeManager(space, self.baseTrie)
# Now coerrce myself into being properly typed.
myTypeUid = space.cross(self.root, self.typeSystem.typeAttr)
if myTypeUid == ANOIBaseSpace.NIL:
space.crossEquals(self.root, self.typeSystem.typeAttr,
self.typeUid)
space.crossEquals(self.root, self.ref, ANOIBaseSpace.NIL)
space.crossEquals(self.root, self.parent, ANOIBaseSpace.NIL)
else:
assert myTypeUid == self.typeUid
# ____________________________________________________________
def copySimpleTrie (self, simpleTrieObj):
"""ANOITrie.copySimpleTrie()
"""
if __DEBUG__:
print "ANOITrie.copySimpleTrie():", simpleTrieObj
simpleTrieDict = simpleTrieObj.getVectors()
for key, value in simpleTrieDict.items():
if __DEBUG__:
print `ANOIUtil.uidVecToStr(key)`, "=>", value
self.setVector(list(key), value)
# ____________________________________________________________
def getNameOrCreate (self, name):
"""ANOITrie.getNameOrCreate()
Should only be used for bootstrap
"""
retVal = self.getName(name)
if retVal == ANOIBaseSpace.NIL:
retVal = self.space.getUid()
self.setName(name, retVal)
return retVal
# ____________________________________________________________
def createTrieNode (self, space, prevUid, keyUid):
"""ANOITrie.createTrieNode()
"""
if __DEBUG__:
print self, "createTrieNode(..., %d, %d)" % (prevUid, keyUid)
assert space == self.space
retVal = self.typeSystem.createInstance(self.typeUid)
self.space.crossEquals(retVal, self.ref, ANOIBaseSpace.NIL)
self.space.crossEquals(retVal, self.parent, prevUid)
self.space.crossEquals(prevUid, keyUid, retVal)
return retVal
# ____________________________________________________________
def getVectors (self, crntUid = None, crntVec = None, visited = None,
result = None):
"""ANOITrie.getVectors()
"""
retVal = {}
if __DEBUG__:
print "ANOITrie.getVectors():", crntUid, crntVec
if crntUid == None:
crntUid = self.root
if crntVec == None:
crntVec = [crntUid]
if visited == None:
visited = []
if result == None:
result = {}
if crntUid in visited:
if __DEBUG__:
print "ANOITrie.getVectors(): Warning! Circular trie key!"
print "\t", crntUid, crntVec, visited
else:
visited.append(crntUid)
keyUidList = self.space.getUidKeys(crntUid)
for keyUid in keyUidList:
if keyUid == self.ref:
referencedUid = self.space.cross(crntUid, keyUid)
if referencedUid != ANOIBaseSpace.NIL:
result[tuple(crntVec[1:])] = referencedUid
else:
nextUid = self.space.cross(crntUid, keyUid)
if self.typeSystem.getType(nextUid) == self.typeUid:
crntVec.append(keyUid)
self.getVectors(nextUid, crntVec, visited, result)
del crntVec[-1]
return result
# ____________________________________________________________
def compressVector (self, uidVec):
"""ANOITrie.compressVector()
"""
retVal = []
cross = self.space.cross
NIL = ANOIBaseSpace.NIL
i = 0
uidVecLen = len(uidVec)
while i < uidVecLen:
crntUid = cross(self.root, uidVec[i])
lastGoodRef = NIL
lastGoodPos = i
j = i + 1
# At loop entry, b/c i < uidVecLen, j = i + 1 <= uidVecLen
# To avoid redundant comparisons, the uidVecLen guard is handled
# below, using break.
while (NIL != crntUid):
crntRef = cross(crntUid, self.ref)
if NIL != crntRef:
lastGoodRef = crntRef
lastGoodPos = j
if j < uidVecLen:
crntUid = cross(crntUid, uidVec[j])
else:
break
j = j + 1
if lastGoodRef == NIL:
retVal.append(uidVec[i])
i = i + 1
else:
retVal.append(lastGoodRef)
i = lastGoodPos
return retVal
# ______________________________________________________________________
def getNamespace (space, rootNS):
retVal = None
rootNSType = type(rootNS)
if rootNSType == types.IntType:
retVal = ANOITrie(space, rootNS)
else:
assert rootNSType in (ANOISimpleTrie, ANOITrie)
retVal = rootNS
return retVal
# ______________________________________________________________________
# End of ANOITrie.py