'''
Generic Hierarchic Objects Module
Hierobj's store their children (which can be anything) in their
__childlist__ attribute, and provide methods for walking the 
hierarchy, either collecting results or not.

The index function returns an index of the objects (effectively a
flattened copy of the hierarchy)

97-03-17 Added ability to pass arguments to hier_rapply and hier_rreturn.
97-10-31 Removed dependencies on mcf.store
'''
import copy,types
import singletonlist, hier_rx

class Hierobj: 
	'''
	An abstract class which handles hierarchic functions and information
	# remade as a DAG 97-04-02, also reduced memory overhead for
	hier-r* functions by using while-del-IndexError construct versus
	for loop (probably makes it slower though)
	If you require a true hierarchy, use the TrueHierobj class below...
	'''
	def __init__(self, parent=None, childlist=None):
		if parent is None: # passed no parents
			self.__dict__['__parent__'] = []
		elif type(parent) == types.ListType: # passed a list of parents
			self.__dict__['__parent__'] = parent
		else: # passed a single parent
			self.__dict__['__parent__'] = [parent]
		self.__dict__['__childlist__'] = childlist or []
		for child in self.__childlist__:
			try:
				child.__parent__.append(self)
			except:
				pass
	# import simple hierarchic processing methods
	hier_rapply = hier_rx.hier_rapply
	hier_rreturn = hier_rx.hier_rreturn
	hier_rgetattr = hier_rx.hier_rgetattr
	hier_rmethod = hier_rx.hier_rmethod


	def hier_addchild(self, child):
		'''
		Add a single child to the childlist
		'''
		self.__childlist__.append(child)
		try:
			# Hierobj-aware child
			child.__parent__.append(self) # raises error if not hier_obj aware
		except (TypeError, AttributeError):
			# Non Hierobj-aware child
			pass
	append = hier_addchild
	def hier_remchild(self, child):
		'''
		Breaks the child relationship with child, including the
		reciprocal parent relationship
		'''
		try:
			self.__childlist__.remove(child)
			try:
				child.hier_remparent(self) # if this fails, no problem
			except AttributeError: pass
		except (AttributeError,ValueError):
			return 0 # didn't manage to remove the child
		return 1 # succeeded
	def hier_remparent(self, parent):
		'''
		Normally only called by hier_remchild of the parent,
		just removes the parent from the child's parent list,
		but leaves child in parent's childlist
		'''
		try:
			self.__parent__.remove(parent)
		except (AttributeError,ValueError):
			return 0
		return 1
	def hier_replacewith(self,newel):
		'''
		As far as the hierarchy is concerned, the new element
		is exactly the same as the old element, it has all
		the same children, all the same parents.  The old
		element becomes completely disconnected from the hierarchy,
		but it still retains all of its references
		
		For every parent, replace this as a child
		For every child, replace this as the parent
		'''
		for parent in self.__parent__:
			try:
				parent.hier_replacechild(self, newel)
			except AttributeError:
				pass
		for child in self.__childlist__:
			try:
				child.hier_replaceparent(self,parent)
			except AttributeError:
				pass
	def hier_replaceparent(self, oldparent, newparent):
		ind = self.__parent__.index(oldparent)
		self.__parent__[ind] = newparent
	def hier_replacechild(self, oldchild, newchild):
		ind = self.__childlist__.index(oldchild)
		self.__childlist__[ind] = newchild
		
class TrueHierobj(Hierobj):
	'''
	An inefficient implementation of an Hierobj which limits the
	__parent__ attribute to a single element.  This will likely be
	_slower_ than an equivalent Hierobj.  That will have to be fixed
	eventually.
	'''
	def __init__(self, parent=None, childlist=[]):
		if parent is None: # passed no parents
			self.__dict__['__parent__'] = singletonlist.SingletonList()
		else: # passed a single parent
			self.__dict__['__parent__'] = singletonlist.SingletonList(parent)
		self.__dict__['__childlist__'] = copy.copy(childlist)
		for child in self.__childlist__:
			try:
				child.__parent__.append(self)
			except:
				pass

def index(grove):
	'''
	Returns a flattened version of the grove
	'''
	return grove.hier_rreturn(lambda x: x)


