# -*- test-case-name: mv3d.test.test_container -*- # Copyright (C) 2006-2012 Mortal Coil Games # See LICENSE for details. """ A General Container Class """ from twisted.internet import defer from mv3d.util.persist import IDTuple, List class ContainerError(Exception): """ An error for when something goes wrong with a container """ class Container(object): """ You need to add a docstring here ! """ iid = IDTuple(autoSave=True, partialSave=True) contained = List(IDTuple()) containedby = List(IDTuple()) allowedState = ["iid", "contained", "containedby"] def __init__(self): self.contained = [] self.containedby = [] def setupContainer(self): """ Set up the container's attributes """ if self.contained is None: self.contained = [] self.containedby = [] def updateContainerObservers(self, wh, *a, **kw): """ If we are observable, then update the observers """ if hasattr(self, "updateAllClients"): self.updateAllClients(wh, *a, **kw) def __getitem__(self, iid): return self.get(iid) def __len__(self): return self.countContents() def setID(self, iid): """ Set the id of this container """ self.iid = iid self.updateContainerObservers('setID', iid) def getID(self): """ Get the id of this container """ return self.iid def contain(self, object): """ Put object inside us. (eww) Only called on master """ if hasattr(self, "replacedby"): raise ContainerError("This container has been replaced!") if hasattr(self, "getPriority") and self.getPriority() != 1: if isinstance(object, Container): oid = object.getID() else: oid = object #print "Calling manipulate contain" return self.getManipulator().callRemote( "Manipulate", "contain", oid) #print "Calling local Contain",oid, self.getPriority(),self.getID() return self._contain(object) def _contain(self, object): """ Contain this object (on slave and master) """ self.setupContainer() #print "Calling _Contain" def gotItem(object, oid): if isinstance(object, int): raise ContainerError("Didn't get the item to contain.") self.contained.append(oid) d = 1 if not hasattr(self, "getPriority") or self.getPriority() == 1: d = object.addContainedBy(self) self.updateContainerObservers('contain', oid) if hasattr(self, "save"): self.queueSave(selectAttributes=["contained"]) return d if isinstance(object, Container): oid = object.getID() assert oid is not None, "Object's id can not be none." if oid in self.contained: raise ContainerError( "Containing something we already contain.. %s" % oid) else: oid = object assert oid is not None, "Object's id can not be none." if oid in self.contained: raise ContainerError( "Containing something we already contain..") d = self.parent.getItem(oid) d.addCallback(gotItem, oid) return d return gotItem(object, oid) observe_contain = _contain def takeOut(self, object): """ Remove an object from inside this container (only on master) """ if hasattr(self, "getPriority") and self.getPriority() != 1: if isinstance(object, Container): oid = object.getID() else: oid = object return self.getManipulator().callRemote("manipulate", "takeOut", oid) return self._takeOut(object) def _takeOut(self, object): """ Remove an object from inside this container (master or slave) """ self.setupContainer() def gotItem(object, oid): self.contained.remove(oid) if not hasattr(self, "getPriority") or self.getPriority() == 1: object.remContainedBy(self) self.updateContainerObservers('takeOut', oid) if hasattr(self, "save"): self.queueSave(selectAttributes=["contained"]) return 1 if isinstance(object, Container): oid = object.getID() else: oid = object d = self.parent.getItem(oid) d.addCallback(gotItem, oid) return d return gotItem(object, oid) observe_takeOut = _takeOut def getContents(self): """ Returns all the items in this container """ self.setupContainer() d = [] ret = {} for c in self.contained: r = self.getItem(c) if isinstance(r, defer.Deferred): d.append(r) else: ret[r.getID()] = r def gotItems(r): for i in r: ret[i.getID()] = i return ret if len(d): dl = defer.gatherResults(d) dl.addCallback(gotItems) return dl return ret def getContainers(self): """ Returns all the objects that contain us """ self.setupContainer() d = [] ret = {} for c in self.containedby: r = self.getItem(c) if isinstance(r, defer.Deferred): d.append(r) else: ret[r.getID()] = r def gotItems(r): for i in r: ret[i.getID()] = i return ret if len(d): dl = defer.gatherResults(d) return dl.addCallback(gotItems) return ret def listContents(self): """ Just return the list of items we contain """ self.setupContainer() return self.contained def empty(self): """ Remove all items we contain """ self.setupContainer() d = [] for c in self.contained: dd = self.getItem(c) if isinstance(dd, defer.Deferred): dd.addCallback(lambda i: i.remContainedBy(self)) d.append(dd) else: dd.remContainedBy(self) self.contained = {} if hasattr(self, "save"): self.queueSave(selectAttributes=["contained"]) self.updateContainerObservers('empty') if len(d): return defer.gatherResults(d) return 1 observe_empty = empty def isEmpty(self): """ Return true if we don't contain anything """ self.setupContainer() return len(self.contained) == 0 def countContents(self, r=False): """ Return the count of the contents. If R is true, recursively count contents """ self.setupContainer() cts = len(self.contained) if not r: return cts for c in self.contained: cts += c.countContents(1) return cts def getContained(self, iid): """ Get an item contained by this container """ self.setupContainer() if not self.contained.has_key(iid): raise ContainerError( "There isn't a %s in this container." % str(iid)) return self.contained[iid] def listContainedBy(self): """ Return the list of items that contain us """ self.setupContainer() return self.containedby def addContainedBy(self, a): """ Add a container to this item (master only) """ #print "Calling addContainedBy" self.setupContainer() if isinstance(a, Container): oid = a.getID() else: oid = a if oid in self.containedby: raise ContainerError("This object is already contained by %s" % (oid,)) if hasattr(self, "getPriority") and self.getPriority() != 1: return self.getManipulator().callRemote("manipulate", "addContainedBy", oid) return self._addContainedBy(a) def _addContainedBy(self, a): """ Add a container to this item (master or slave) """ #print "Calling _addContainedBy" self.setupContainer() if isinstance(a, Container): oid = a.getID() else: oid = a if oid in self.containedby: raise ContainerError("This object is already contained") self.containedby.append(oid) if hasattr(self, "save"): self.queueSave(selectAttributes=["containedby"]) self.updateContainerObservers('addContainedBy', oid) #print self.getID(),"added containedby",oid return True observe_addContainedBy = _addContainedBy def remContainedBy(self, a): """ You need to add a docstring here ! """ #print "remContainedBy" self.setupContainer() if isinstance(a, Container): oid = a.getID() else: oid = a if not oid in self.containedby: raise ContainerError("This object isn't contained by us") if hasattr(self, "getPriority") and self.getPriority() != 1: self.getManipulator().callRemote("manipulate", "remContainedBy", oid) return self._remContainedBy(a) def _remContainedBy(self, a): """ You need to add a docstring here ! """ #print "_remContainedBy" self.setupContainer() if isinstance(a, Container): oid = a.getID() else: oid = a if not oid in self.containedby: raise ContainerError("That object isn't contained by us") self.containedby.remove(oid) if hasattr(self, "save"): self.queueSave(selectAttributes=["containedby"]) self.updateContainerObservers('remContainedBy', oid) return 1 observe_remContainedBy = _remContainedBy def existsIn(self, iid): """ Recurse through sub objects and see if object with id of i is in there """ self.setupContainer() if iid in self.contained: return True d = [] for c in self.contained: dd = self.getItem(c) if isinstance(dd, defer.Deferred): dd.addCallback(lambda itm: itm.existsIn(iid)) d.append(dd) else: di = dd.existsIn(iid) if di: return True if isinstance(di, defer.Deferred): d.append(di) if len(d): dl = defer.gatherResults(d) def gotList(r): return True in r dl.addCallback(gotList) return dl return False def findIn(self, i): """ If object with id i exists in this object, return it, otherwise, False """ ei = self.existsIn(i) if isinstance(ei, defer.Deferred): def gotAnswer(a): if a: return self.getItem(i) return False return ei.addCallback(gotAnswer) if ei: return self.getItem(i) return ei def replaceContainer(self, oldo): """ You need to add a docstring here ! """ self.setupContainer() for c in oldo.contained.values(): oldo.takeOut(c) # keep references to objects that # are still alive or are primary to us if c.getPriority() == 1 or not c.hasLostMaster(): if self.contained.has_key(c.getID()): self.takeOut(c.getID()) self.contain(c) for c in oldo.containedby: c.takeOut(oldo) c.contain(self)