123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- """
- A class which defines a composite object which can store
- hieararchical dictionaries with names.
- This class is same as a hiearchical dictionary, but it
- provides methods to add/access/modify children by name,
- like a Composite.
- Created Anand B Pillai <abpillai@gmail.com>
- """
- __author__ = "Anand B Pillai"
- __maintainer__ = "Anand B Pillai"
- __version__ = "0.2"
- def normalize(val):
- """ Normalize a string so that it can be used as an attribute
- to a Python object """
- if val.find('-') != -1:
- val = val.replace('-', '_')
- return val
- def denormalize(val):
- """ De-normalize a string """
- if val.find('_') != -1:
- val = val.replace('_', '-')
- return val
- class SpecialDict(dict):
- """ A dictionary type which allows direct attribute
- access to its keys """
- def __getattr__(self, name):
- if name in self.__dict__:
- return self.__dict__[name]
- elif name in self:
- return self.get(name)
- else:
- # Check for denormalized name
- name = denormalize(name)
- if name in self:
- return self.get(name)
- else:
- raise AttributeError('no attribute named %s' % name)
- def __setattr__(self, name, value):
- if name in self.__dict__:
- self.__dict__[name] = value
- elif name in self:
- self[name] = value
- else:
- # Check for denormalized name
- name2 = denormalize(name)
- if name2 in self:
- self[name2] = value
- else:
- # New attribute
- self[name] = value
- class CompositeDict(SpecialDict):
- """ A class which works like a hierarchical dictionary.
- This class is based on the Composite design-pattern """
- ID = 0
- def __init__(self, name=''):
- if name:
- self._name = name
- else:
- self._name = ''.join(('id#', str(self.__class__.ID)))
- self.__class__.ID += 1
- self._children = []
- # Link back to father
- self._father = None
- self[self._name] = SpecialDict()
- def __getattr__(self, name):
- if name in self.__dict__:
- return self.__dict__[name]
- elif name in self:
- return self.get(name)
- else:
- # Check for denormalized name
- name = denormalize(name)
- if name in self:
- return self.get(name)
- else:
- # Look in children list
- child = self.findChild(name)
- if child:
- return child
- else:
- attr = getattr(self[self._name], name)
- if attr:
- return attr
- raise AttributeError('no attribute named %s' % name)
- def isRoot(self):
- """ Return whether I am a root component or not """
- # If I don't have a parent, I am root
- return not self._father
- def isLeaf(self):
- """ Return whether I am a leaf component or not """
- # I am a leaf if I have no children
- return not self._children
- def getName(self):
- """ Return the name of this ConfigInfo object """
- return self._name
- def getIndex(self, child):
- """ Return the index of the child ConfigInfo object 'child' """
- if child in self._children:
- return self._children.index(child)
- else:
- return -1
- def getDict(self):
- """ Return the contained dictionary """
- return self[self._name]
- def getProperty(self, child, key):
- """ Return the value for the property for child
- 'child' with key 'key' """
- # First get the child's dictionary
- childDict = self.getInfoDict(child)
- if childDict:
- return childDict.get(key, None)
- def setProperty(self, child, key, value):
- """ Set the value for the property 'key' for
- the child 'child' to 'value' """
- # First get the child's dictionary
- childDict = self.getInfoDict(child)
- if childDict:
- childDict[key] = value
- def getChildren(self):
- """ Return the list of immediate children of this object """
- return self._children
- def getAllChildren(self):
- """ Return the list of all children of this object """
- l = []
- for child in self._children:
- l.append(child)
- l.extend(child.getAllChildren())
- return l
- def getChild(self, name):
- """ Return the immediate child object with the given name """
- for child in self._children:
- if child.getName() == name:
- return child
- def findChild(self, name):
- """ Return the child with the given name from the tree """
- # Note - this returns the first child of the given name
- # any other children with similar names down the tree
- # is not considered.
- for child in self.getAllChildren():
- if child.getName() == name:
- return child
- def findChildren(self, name):
- """ Return a list of children with the given name from the tree """
- # Note: this returns a list of all the children of a given
- # name, irrespective of the depth of look-up.
- children = []
- for child in self.getAllChildren():
- if child.getName() == name:
- children.append(child)
- return children
- def getPropertyDict(self):
- """ Return the property dictionary """
- d = self.getChild('__properties')
- if d:
- return d.getDict()
- else:
- return {}
- def getParent(self):
- """ Return the person who created me """
- return self._father
- def __setChildDict(self, child):
- """ Private method to set the dictionary of the child
- object 'child' in the internal dictionary """
- d = self[self._name]
- d[child.getName()] = child.getDict()
- def setParent(self, father):
- """ Set the parent object of myself """
- # This should be ideally called only once
- # by the father when creating the child :-)
- # though it is possible to change parenthood
- # when a new child is adopted in the place
- # of an existing one - in that case the existing
- # child is orphaned - see addChild and addChild2
- # methods !
- self._father = father
- def setName(self, name):
- """ Set the name of this ConfigInfo object to 'name' """
- self._name = name
- def setDict(self, d):
- """ Set the contained dictionary """
- self[self._name] = d.copy()
- def setAttribute(self, name, value):
- """ Set a name value pair in the contained dictionary """
- self[self._name][name] = value
- def getAttribute(self, name):
- """ Return value of an attribute from the contained dictionary """
- return self[self._name][name]
- def addChild(self, name, force=False):
- """ Add a new child 'child' with the name 'name'.
- If the optional flag 'force' is set to True, the
- child object is overwritten if it is already there.
- This function returns the child object, whether
- new or existing """
- if type(name) != str:
- raise ValueError('Argument should be a string!')
- child = self.getChild(name)
- if child:
- # print('Child %s present!' % name)
- # Replace it if force==True
- if force:
- index = self.getIndex(child)
- if index != -1:
- child = self.__class__(name)
- self._children[index] = child
- child.setParent(self)
- self.__setChildDict(child)
- return child
- else:
- child = self.__class__(name)
- child.setParent(self)
- self._children.append(child)
- self.__setChildDict(child)
- return child
- def addChild2(self, child):
- """ Add the child object 'child'. If it is already present,
- it is overwritten by default """
- currChild = self.getChild(child.getName())
- if currChild:
- index = self.getIndex(currChild)
- if index != -1:
- self._children[index] = child
- child.setParent(self)
- # Unset the existing child's parent
- currChild.setParent(None)
- del currChild
- self.__setChildDict(child)
- else:
- child.setParent(self)
- self._children.append(child)
- self.__setChildDict(child)
- if __name__ == "__main__":
- window = CompositeDict('Window')
- frame = window.addChild('Frame')
- tfield = frame.addChild('Text Field')
- tfield.setAttribute('size', '20')
- btn = frame.addChild('Button1')
- btn.setAttribute('label', 'Submit')
- btn = frame.addChild('Button2')
- btn.setAttribute('label', 'Browse')
- # print(window)
- # print(window.Frame)
- # print(window.Frame.Button1)
- # print(window.Frame.Button2)
- print(window.Frame.Button1.label)
- print(window.Frame.Button2.label)
- ### OUTPUT ###
- # Submit
- # Browse
|