Package Products :: Package ZenRelations :: Module ToManyContRelationship |
[hide private]
[frames] | no frames]

# Source Code for Module Products.ZenRelations.ToManyContRelationship

      1  ###########################################################################
      2  #
      3  # This program is part of Zenoss Core, an open source monitoring platform.
      4  # Copyright (C) 2007, Zenoss Inc.
      5  #
      6  # This program is free software; you can redistribute it and/or modify it
      7  # under the terms of the GNU General Public License version 2 or (at your
      8  # option) any later version as published by the Free Software Foundation.
      9  #
     10  # For complete information please visit: http://www.zenoss.com/oss/
     11  #
     12  ###########################################################################
     13
     14  __doc__ = """ToManyContRelationship
     15  A to-many container relationship
     16  """
     17
     18
     19  import logging
     20  log = logging.getLogger("zen.Relations")
     21
     22  from Globals import DTMLFile
     23  from Globals import InitializeClass
     24  from AccessControl import ClassSecurityInfo
     25  from Acquisition import aq_base
     26  from OFS.ObjectManager import checkValidId, BeforeDeleteException
     27  from ZODB.POSException import ConflictError
     28
     29  import OFS.subscribers
     30  from OFS.event import ObjectWillBeAddedEvent
     31  from OFS.event import ObjectWillBeRemovedEvent
     32  from zope.event import notify
     33  from zope.container.contained import ObjectAddedEvent
     34  from zope.container.contained import ObjectRemovedEvent
     35  from zope.container.contained import dispatchToSublocations
     36
     37  from BTrees.OOBTree import OOBTree
     38
     39  from ToManyRelationshipBase import ToManyRelationshipBase
     40
     41  from Products.ZenRelations.Exceptions import *
     42
     43  from Products.ZenUtils.Utils import unused
     44
     45 -def manage_addToManyContRelationship(context, id, REQUEST=None):
     46      """factory for ToManyRelationship"""
     47      rel =  ToManyContRelationship(id)
     48      context._setObject(rel.id, rel)
     49      if REQUEST:
     50          REQUEST['RESPONSE'].redirect(context.absolute_url()+'/manage_main')
     51      return rel.id
     52
     53
     54  addToManyContRelationship = DTMLFile('dtml/addToManyContRelationship',globals())
     55
     56
     57 -class ToManyContRelationship(ToManyRelationshipBase):
     58      """
     59      ToManyContRelationship is the ToMany side of a realtionship that
     60      contains its related objects (like the normal Zope ObjectManager)
     61      """
     62
     63      meta_type = "ToManyContRelationship"
     64
     65      security = ClassSecurityInfo()
     66
     67
     68 -    def __init__(self, id):
     69          """set our instance values"""
     70          self.id = id
     71          self._objects = OOBTree()
     72
     73
     74 -    def _safeOfObjects(self):
     75          """
     76          Try to safely return ZenPack objects rather than
     77          causing imports to fail.
     78          """
     79          objs = []
     80          for ob in self._objects.values():
     81              try:
     82                  objs.append(ob.__of__(self))
     83              except AttributeError:
     84                  log.info("Ignoring unresolvable object '%s'", str(ob))
     85          return objs
     86
     87 -    def __call__(self):
     88          """when we are called return our related object in our aq context"""
     89          return self._safeOfObjects()
     90
     91
     92 -    def __getattr__(self, name):
     93          """look in the two object stores for related objects"""
     94          if '_objects' in self.__dict__:
     95              objects = self._objects
     96              if objects.has_key(name): return objects[name]
     97          raise AttributeError( "Unable to find the attribute '%s'" % name )
     98
     99
    100 -    def __hasattr__(self, name):
    101          """check to see if we have an object by an id
    102          this will fail if passed a short id and object is stored
    103          with fullid (ie: it is related not contained)
    104          use hasobject to get around this issue"""
    105          return self._objects.has_key(name)
    106
    107
    108 -    def hasobject(self, obj):
    109          "check to see if we have this object"
    110          return self._objects.get(obj.id) == obj
    111
    112
    113 -    def addRelation(self, obj):
    114          """Override base to run manage_afterAdd like ObjectManager"""
    115          if self._objects.has_key(obj.getId()):
    116              log.debug("obj %s already exists on %s", obj.getPrimaryId(),
    117                          self.getPrimaryId())
    118
    119          notify(ObjectWillBeAddedEvent(obj, self, obj.getId()))
    120          ToManyRelationshipBase.addRelation(self, obj)
    121          obj = obj.__of__(self)
    122          o = self._getOb(obj.id)
    123          notify(ObjectAddedEvent(o, self, obj.getId()))
    124
    125
    126 -    def _setObject(self,id,object,roles=None,user=None,set_owner=1):
    127          """ObjectManager interface to add contained object."""
    128          unused(user, roles, set_owner)
    129          object.__primary_parent__ = aq_base(self)
    130          self.addRelation(object)
    131          return object.getId()
    132
    133
    134 -    def manage_afterAdd(self, item, container):
    135          # Don't do recursion anymore, a subscriber does that.
    136          pass
    137      manage_afterAdd.__five_method__ = True
    138
    139 -    def manage_afterClone(self, item):
    140          # Don't do recursion anymore, a subscriber does that.
    141          pass
    142      manage_afterClone.__five_method__ = True
    143
    144 -    def manage_beforeDelete(self, item, container):
    145          # Don't do recursion anymore, a subscriber does that.
    146          pass
    147      manage_beforeDelete.__five_method__ = True
    148
    149 -    def _add(self,obj):
    150          """add an object to one side of a ToManyContRelationship.
    151          """
    152          id = obj.id
    153          if self._objects.has_key(id):
    154              raise RelationshipExistsError
    155          v=checkValidId(self, id)
    156          if v is not None: id=v
    157          self._objects[id] = aq_base(obj)
    158          obj = aq_base(obj).__of__(self)
    159          self.setCount()
    160
    161
    162 -    def _remove(self, obj=None, suppress_events=False):
    163          """remove object from our side of a relationship"""
    164          if obj: objs = [obj]
    165          else: objs = self.objectValuesAll()
    166          if not suppress_events:
    167              for robj in objs:
    168                  notify(ObjectWillBeRemovedEvent(robj, self, robj.getId()))
    169          if obj:
    170              id = obj.id
    171              if not self._objects.has_key(id):
    172                  raise ObjectNotFound(
    173                      "object %s not found on %s" % (
    174                      obj.getPrimaryId(), self.getPrimaryId()))
    175              del self._objects[id]
    176          else:
    177              self._objects = OOBTree()
    178              self.__primary_parent__._p_changed = True
    179          if not suppress_events:
    180              for robj in objs:
    181                  notify(ObjectRemovedEvent(robj, self, robj.getId()))
    182          self.setCount()
    183
    184
    185 -    def _remoteRemove(self, obj=None):
    186          """remove an object from the far side of this relationship
    187          if no object is passed in remove all objects"""
    188          if obj:
    189              if not self._objects.has_key(obj.id):
    190                  raise ObjectNotFound("object %s not found on %s" % (
    191                      obj.getPrimaryId(), self.getPrimaryId()))
    192              objs = [obj]
    193          else: objs = self.objectValuesAll()
    194          remoteName = self.remoteName()
    195          for obj in objs:
    196              rel = getattr(obj, remoteName)
    197              rel._remove(self.__primary_parent__)
    198
    199
    200 -    def _getOb(self, id, default=zenmarker):
    201          """look up in our local store and wrap in our aq_chain"""
    202          if self._objects.has_key(id):
    203              return self._objects[id].__of__(self)
    204          elif default == zenmarker:
    205              raise AttributeError( "Unable to find %s" % id )
    206          return default
    207
    208
    209      security.declareProtected('View', 'objectIds')
    210 -    def objectIds(self, spec=None):
    211          """only return contained objects"""
    212          if spec:
    213              if isinstance(spec,basestring): spec=[spec]
    214              return [obj.id for obj in self._objects.values() \
    215                          if obj.meta_type in spec]
    216          return [ k for k in self._objects.keys() ]
    217      objectIdsAll = objectIds
    218
    219
    220      security.declareProtected('View', 'objectValues')
    221 -    def objectValues(self, spec=None):
    222          """override to only return owned objects for many to many rel"""
    223          if spec:
    224              if isinstance(spec,basestring): spec=[spec]
    225              return [ob.__of__(self) for ob in self._objects.values() \
    226                          if ob.meta_type in spec]
    227          return self._safeOfObjects()
    228      security.declareProtected('View', 'objectValuesAll')
    229      objectValuesAll = objectValues
    230
    231
    232 -    def objectValuesGen(self):
    233          """Generator that returns all related objects."""
    234          for obj in self._objects.values():
    235              yield obj.__of__(self)
    236
    237
    238 -    def objectItems(self, spec=None):
    239          """over ride to only return owned objects for many to many rel"""
    240          if spec:
    241              if isinstance(spec,basestring): spec=[spec]
    242              return [(key,value.__of__(self)) \
    243                  for (key,value) in self._objects.items() \
    244                      if value.meta_type in spec]
    245          return [(key,value.__of__(self)) \
    246                      for (key,value) in self._objects.items()]
    247      objectItemsAll = objectItems
    248
    249
    250  #FIXME - need to make this work
    251  #    def all_meta_types(self, interfaces=None):
    252  #        mts = []
    253  #        for mt in ToManyRelationshipBase.all_meta_types(self, interfaces):
    254  #            if (mt.has_key('instance') and mt['instance']):
    255  #                for cl in self.sub_classes:
    256  #                    if checkClass(mt['instance'], cl):
    257  #                        mts.append(mt)
    258  #        return mts
    259
    260
    261 -    def _getCopy(self, container):
    262          """
    263          make new relation add copies of contained objs
    264          and refs if the relation is a many to many
    265          """
    266          rel = self.__class__(self.id)
    267          rel.__primary_parent__ = container
    268          rel = rel.__of__(container)
    269          norelcopy = getattr(self, 'zNoRelationshipCopy', [])
    270          if self.id in norelcopy: return rel
    271          for oobj in self.objectValuesAll():
    272              cobj = oobj._getCopy(rel)
    273              rel._setObject(cobj.id, cobj)
    274          return rel
    275
    276 -    def checkValidId(self, id):
    277          """
    278          Is this a valid id for this container?
    279          """
    280          try:
    281              checkValidId(self, id)
    282          except:
    283              raise
    284          else:
    285              return True
    286
    287 -    def exportXml(self, ofile, ignorerels=[]):
    288          """Return an xml representation of a ToManyContRelationship
    289          <tomanycont id='interfaces'>
    290              <object id='hme0'
    291                  module='Products.Confmon.IpInterface' class='IpInterface'>
    292                  <property></property> etc....
    293              </object>
    294          </tomanycont>
    295          """
    296          if self.countObjects() == 0: return
    297          ofile.write("<tomanycont id='%s'>\n" % self.id)
    298          for obj in self.objectValues():
    299              obj.exportXml(ofile, ignorerels)
    300          ofile.write("</tomanycont>\n")
    301
    302
    303 -    def checkRelation(self, repair=False):
    304          """Check to make sure that relationship bidirectionality is ok.
    305          """
    306          if len(self._objects):
    307              log.debug("checking relation: %s", self.id)
    308          else:
    309              return
    310
    311          # look for objects that don't point back to us
    312          # or who should no longer exist in the database
    313          remoteName = self.remoteName()
    314          parentObject = self.getPrimaryParent()
    315          for obj in self._objects.values():
    316              rrel = getattr(obj, remoteName)
    317              if not rrel.hasobject(parentObject):
    318                  log.error("remote relation %s doesn't point back to %s",
    319                                  rrel.getPrimaryId(), self.getPrimaryId())
    320                  if repair:
    321                      log.warn("reconnecting relation %s to relation %s",
    322                              rrel.getPrimaryId(),self.getPrimaryId())
    323                      rrel._add(parentObject)
    324
    325
    326
    327  InitializeClass(ToManyContRelationship)
    328
    329
    330 -class ToManyContSublocations(object):
    331      """
    332      Adapter so the event dispatching can propagate to children.
    333      """
    334 -    def __init__(self, container):
    335          self.container = container
    336 -    def sublocations(self):
    337          return (ob for ob in self.container.objectValuesAll())
    338