#  $Id$
#
#  Copyright (C) 2003-2006 Rational Discovery LLC
#     All Rights Reserved
#
import sys

class VLibNode(object):
  """ base class for all virtual library nodes,
  defines minimal required interface

  """
  def __init__(self,*args,**kwargs):
    self._children = []
    self._parents = []

  #------------------------------------
  #
  #  Iteration
  #
  def __iter__(self):
    """ part of the iterator interface """
    self.reset()
    return self
  def next(self):
    """ part of the iterator interface

      raises StopIteration on failure
    """
    pass
  def reset(self):
    """ resets our iteration state

    """
    for parent in self.GetParents():
      parent.reset()


  #------------------------------------
  #
  #  Library graph operations
  #  Probably most of these won't need to be reimplemented in
  #  child classes
  #
  def AddChild(self,child,notify=1):
    """

    >>> p1 = VLibNode()
    >>> p2 = VLibNode()
    >>> c1 = VLibNode()
    >>> p1.AddChild(c1)
    >>> len(c1.GetParents())
    1
    >>> len(p1.GetChildren())
    1
    >>> p2.AddChild(c1,notify=0)
    >>> len(c1.GetParents())
    1
    >>> len(p2.GetChildren())
    1
    >>> c1.AddParent(p2,notify=0)
    >>> len(c1.GetParents())
    2
    >>> len(p2.GetChildren())
    1

    """
    self._children.append(child)
    if notify:
      child.AddParent(self,notify=0)
  def RemoveChild(self,child,notify=1):
    """
    >>> p1 = VLibNode()
    >>> c1 = VLibNode()
    >>> p1.AddChild(c1)
    >>> len(c1.GetParents())
    1
    >>> len(p1.GetChildren())
    1
    >>> p1.RemoveChild(c1)
    >>> len(c1.GetParents())
    0
    >>> len(p1.GetChildren())
    0
    """
    self._children.remove(child)
    if notify:
      child.RemoveParent(self,notify=0)
  def GetChildren(self):
    return tuple(self._children)
  
  def AddParent(self,parent,notify=1):
    """
    >>> p1 = VLibNode()
    >>> p2 = VLibNode()
    >>> c1 = VLibNode()
    >>> c1.AddParent(p1)
    >>> len(c1.GetParents())
    1
    >>> len(p1.GetChildren())
    1
    >>> c1.AddParent(p2,notify=0)
    >>> len(c1.GetParents())
    2
    >>> len(p2.GetChildren())
    0
    >>> p2.AddChild(c1,notify=0)
    >>> len(c1.GetParents())
    2
    >>> len(p2.GetChildren())
    1
    """
    self._parents.append(parent)
    if notify:
      parent.AddChild(self,notify=0)
  def RemoveParent(self,parent,notify=1):
    """
    >>> p1 = VLibNode()
    >>> c1 = VLibNode()
    >>> p1.AddChild(c1)
    >>> len(c1.GetParents())
    1
    >>> len(p1.GetChildren())
    1
    >>> c1.RemoveParent(p1)
    >>> len(c1.GetParents())
    0
    >>> len(p1.GetChildren())
    0
    """
    self._parents.remove(parent)
    if notify:
      parent.RemoveChild(self,notify=0)
  def GetParents(self):
    return tuple(self._parents)

  def Destroy(self,notify=1,propagateDown=0,propagateUp=0):
    """
    >>> p1 = VLibNode()
    >>> p2 = VLibNode()
    >>> c1 = VLibNode()
    >>> c2 = VLibNode()
    >>> p1.AddChild(c1)
    >>> p2.AddChild(c1)
    >>> p2.AddChild(c2)
    >>> len(c1.GetParents())
    2
    >>> len(c2.GetParents())
    1
    >>> len(p1.GetChildren())
    1
    >>> len(p2.GetChildren())
    2
    >>> c1.Destroy(propagateUp=1)
    >>> len(p2.GetChildren())
    0
    >>> len(c1.GetParents())
    0
    >>> len(c2.GetParents())
    0
    
    """
    #sys.stderr.write('DESTROY: %s\n'%(str(self)))
    if hasattr(self,'_destroyed'): return
    self._destroyed=1

    if notify:
      for o in self.GetChildren():
        o.RemoveParent(self,notify=0)
        if propagateDown:
          o.Destroy(notify=1,propagateDown=1,propagateUp=propagateUp)
      for o in self.GetParents():
        #sys.stderr.write('\tparent: %s\n'%(str(o)))
        o.RemoveChild(self,notify=0)
        if propagateUp:
          o.Destroy(notify=1,propagateDown=propagateDown,propagateUp=1)
    self._children = []
    self._parents = []
    



#------------------------------------
#
#  doctest boilerplate
#
def _test():
  import doctest,sys
  return doctest.testmod(sys.modules["__main__"])

if __name__ == '__main__':
  import sys
  failed,tried = _test()
  sys.exit(failed)


