1from Attr import Attr
2
3
4class ListAttr(Attr):
5    """This is an attribute that refers to a set of other user-defined objects.
6
7    It cannot include basic data types or instances of classes that are not part
8    of the object model.
9    """
10
11    def __init__(self, attr):
12        Attr.__init__(self, attr)
13        self._className = attr['Type'].rsplit(None, 1)[-1]
14        self._backRefAttr = None  # init'ed in awakeFromRead()
15        if self.get('Min') is not None:
16            self['Min'] = int(self['Min'])
17        if self.get('Max') is not None:
18            self['Max'] = int(self['Max'])
19
20    def className(self):
21        """Return the name of the base class that this obj ref attribute points to."""
22        return self._className
23
24    def backRefAttrName(self):
25        """Return the name of the back-reference attribute in the referenced class.
26
27        It is necessary to be able to override the default back ref     to create
28        data structures like trees, in which a Middle object might reference
29        a parent and multiple children, all of the same class as itself.
30        """
31        assert self._backRefAttr is not None
32        return self._backRefAttr
33
34    def awakeFromRead(self):
35        """Check that the target class and backRefAttr actually exist."""
36        # Check that for "list of Foo", Foo actually exists. And,
37        # Compute self._targetKlass.
38        from Model import ModelError
39        self._targetKlass = self.model().klass(self.className(), None)
40        if not self._targetKlass:
41            raise ModelError('class %s: attr %s:'
42                ' cannot locate target class %s for this list.'
43                % (self.klass().name(), self.name(), self.className()))
44
45        # Compute self._backRefAttr.
46        if 'BackRefAttr' in self:
47            backRefName = self['BackRefAttr']
48        else:
49            backRefName = self.klass().name()
50            attr = self._targetKlass.lookupAttr(backRefName, None)
51            if attr is None:
52                className = self.klass().name()
53                backRefName = className[0].lower() + className[1:]
54        self._backRefAttr = backRefName
55
56        # Check that the backRefAttr, whether explicit or implicit, exists in the target class.
57        backRefAttr = self._targetKlass.lookupAttr(self.backRefAttrName(), None)
58        if backRefAttr is None:
59            raise ModelError('class %s: attr %s: cannot locate backref attr'
60                ' %s.%s for this list.' % (self.klass().name(), self.name(),
61                self.className(), self.backRefAttrName()))
62        backRefAttr['isBackRefAttr'] = True
63