Django

Code

root/django/branches/gis/django/utils/tree.py

Revision 7836, 5.6 kB (checked in by jbronn, 5 months ago)

gis: Merged revisions 7772-7808,7811-7814,7816-7823,7826-7829,7831-7833,7835 via svnmerge from trunk. Modified GeoWhereNode accordingly for changes in r7835.

Line 
1 """
2 A class for storing a tree graph. Primarily used for filter constructs in the
3 ORM.
4 """
5
6 from copy import deepcopy
7
8 class Node(object):
9     """
10     A single internal node in the tree graph. A Node should be viewed as a
11     connection (the root) with the children being either leaf nodes or other
12     Node instances.
13     """
14     # Standard connector type. Clients usually won't use this at all and
15     # subclasses will usually override the value.
16     default = 'DEFAULT'
17
18     def __init__(self, children=None, connector=None, negated=False):
19         """
20         Constructs a new Node. If no connector is given, the default will be
21         used.
22
23         Warning: You probably don't want to pass in the 'negated' parameter. It
24         is NOT the same as constructing a node and calling negate() on the
25         result.
26         """
27         self.children = children and children[:] or []
28         self.connector = connector or self.default
29         self.subtree_parents = []
30         self.negated = negated
31
32     # We need this because of django.db.models.query_utils.Q. Q. __init__() is
33     # problematic, but it is a natural Node subclass in all other respects.
34     def _new_instance(cls, children=None, connector=None, negated=False):
35         """
36         This is called to create a new instance of this class when we need new
37         Nodes (or subclasses) in the internal code in this class. Normally, it
38         just shadows __init__(). However, subclasses with an __init__ signature
39         that is not an extension of Node.__init__ might need to implement this
40         method to allow a Node to create a new instance of them (if they have
41         any extra setting up to do).
42         """
43         obj = Node(children, connector, negated)
44         obj.__class__ = cls
45         return obj
46     _new_instance = classmethod(_new_instance)
47
48     def __str__(self):
49         if self.negated:
50             return '(NOT (%s: %s))' % (self.connector, ', '.join([str(c) for c
51                     in self.children]))
52         return '(%s: %s)' % (self.connector, ', '.join([str(c) for c in
53                 self.children]))
54
55     def __deepcopy__(self, memodict):
56         """
57         Utility method used by copy.deepcopy().
58         """
59         obj = Node(connector=self.connector, negated=self.negated)
60         obj.__class__ = self.__class__
61         obj.children = deepcopy(self.children, memodict)
62         obj.subtree_parents = deepcopy(self.subtree_parents, memodict)
63         return obj
64
65     def __len__(self):
66         """
67         The size of a node if the number of children it has.
68         """
69         return len(self.children)
70
71     def __nonzero__(self):
72         """
73         For truth value testing.
74         """
75         return bool(self.children)
76
77     def __contains__(self, other):
78         """
79         Returns True is 'other' is a direct child of this instance.
80         """
81         return other in self.children
82
83     def add(self, node, conn_type):
84         """
85         Adds a new node to the tree. If the conn_type is the same as the root's
86         current connector type, the node is added to the first level.
87         Otherwise, the whole tree is pushed down one level and a new root
88         connector is created, connecting the existing tree and the new node.
89         """
90         if node in self.children:
91             return
92         if len(self.children) < 2:
93             self.connector = conn_type
94         if self.connector == conn_type:
95             if isinstance(node, Node) and (node.connector == conn_type or
96                     len(node) == 1):
97                 self.children.extend(node.children)
98             else:
99                 self.children.append(node)
100         else:
101             obj = self._new_instance(self.children, self.connector,
102                     self.negated)
103             self.connector = conn_type
104             self.children = [obj, node]
105
106     def negate(self):
107         """
108         Negate the sense of the root connector. This reorganises the children
109         so that the current node has a single child: a negated node containing
110         all the previous children. This slightly odd construction makes adding
111         new children behave more intuitively.
112
113         Interpreting the meaning of this negate is up to client code. This
114         method is useful for implementing "not" arrangements.
115         """
116         self.children = [self._new_instance(self.children, self.connector,
117                 not self.negated)]
118         self.connector = self.default
119
120     def start_subtree(self, conn_type):
121         """
122         Sets up internal state so that new nodes are added to a subtree of the
123         current node. The conn_type specifies how the sub-tree is joined to the
124         existing children.
125         """
126         if len(self.children) == 1:
127             self.connector = conn_type
128         elif self.connector != conn_type:
129             self.children = [self._new_instance(self.children, self.connector,
130                     self.negated)]
131             self.connector = conn_type
132             self.negated = False
133
134         self.subtree_parents.append(self.__class__(self.children,
135                 self.connector, self.negated))
136         self.connector = self.default
137         self.negated = False
138         self.children = []
139
140     def end_subtree(self):
141         """
142         Closes off the most recently unmatched start_subtree() call.
143
144         This puts the current state into a node of the parent tree and returns
145         the current instances state to be the parent.
146         """
147         obj = self.subtree_parents.pop()
148         node = self.__class__(self.children, self.connector)
149         self.connector = obj.connector
150         self.negated = obj.negated
151         self.children = obj.children
152         self.children.append(node)
Note: See TracBrowser for help on using the browser.