slice page working. added temporarily core and util classes from manifold core
[myslice.git] / manifold / util / predicate.py
1 from operator import (
2     and_, or_, inv, add, mul, sub, mod, truediv, lt, le, ne, gt, ge, eq, neg
3     )
4 from manifold.util.misc import contains
5 from types import StringTypes
6
7 # New modifier: { contains 
8 class Predicate:
9
10     operators = {
11         "=="       : eq,
12         "!="       : ne,
13         "<"        : lt,
14         "<="       : le,
15         ">"        : gt,
16         ">="       : ge,
17         "&&"       : and_,
18         "||"       : or_,
19         "contains" : contains
20     }
21
22     operators_short = {
23         "=" : eq,
24         "~" : ne,
25         "<" : lt,
26         "[" : le,
27         ">" : gt,
28         "]" : ge,
29         "&" : and_,
30         "|" : or_,
31         "}" : contains
32     }
33
34     def __init__(self, *args, **kwargs):
35         """
36         \brief Build a predicate (left, operator, right)
37         \param You can pass:
38             - three args (left, operator, right)
39             - one argument (list or tuple) containing three elements (variable, operator, value)
40             "operator" is a String defined in operators or in operators_short and refers
41                 tMao a binary operation.
42             "left" and "right" refers to a variable/constant involved in the Predicate.
43         """
44         if len(args) == 3:
45             key, op, value = args
46         elif len(args) == 1 and isinstance(args[0], (tuple,list)) and len(args[0]) == 3:
47             key, op, value = args[0]
48         elif len(args) == 1 and isinstance(args[0], Predicate):
49             key, op, value = args[0].get_tuple()
50         else:
51             raise Exception, "Bad initializer for Predicate"
52         self.key = key
53         if op in self.operators.keys():
54             self.op = self.operators[op]
55         elif op in self.operators_short.keys():
56             self.op = self.operators_short[op]
57         else:
58             self.op = op
59         if isinstance(value, (list, set)):
60             self.value = tuple(value)
61         else:
62             self.value = value
63
64     def __str__(self):
65         return "Pred(%s, %s, %s)" % self.get_str_tuple()
66
67     def __repr__(self):
68         return self.__str__() 
69
70     def __hash__(self):
71         return hash(self.get_tuple())
72
73     def get_tuple(self):
74         return (self.key, self.op, self.value)
75
76     def get_str_op(self):
77         op_str = [s for s, op in self.operators.iteritems() if op == self.op]
78         return op_str[0]
79
80     def get_str_tuple(self):
81         return (self.key, self.get_str_op(), self.value,)
82
83     def match(self, dic, ignore_missing=False):
84         if isinstance(self.key, tuple):
85             print "PREDICATE MATCH", self.key
86             print dic
87             print "-----------------------------"
88         
89         # Can we match ?
90         if self.key not in dic:
91             return ignore_missing
92
93         if self.op == eq:
94             if isinstance(self.value, list):
95                 return (dic[self.key] in self.value) # array ?
96             else:
97                 return (dic[self.key] == self.value)
98         elif self.op == ne:
99             if isinstance(self.value, list):
100                 return (dic[self.key] not in self.value) # array ?
101             else:
102                 return (dic[self.key] != self.value) # array ?
103         elif self.op == lt:
104             if isinstance(self.value, StringTypes):
105                 # prefix match
106                 return dic[self.key].startswith('%s.' % self.value)
107             else:
108                 return (dic[self.key] < self.value)
109         elif self.op == le:
110             if isinstance(self.value, StringTypes):
111                 return dic[self.key] == self.value or dic[self.key].startswith('%s.' % self.value)
112             else:
113                 return (dic[self.key] <= self.value)
114         elif self.op == gt:
115             if isinstance(self.value, StringTypes):
116                 # prefix match
117                 return self.value.startswith('%s.' % dic[self.key])
118             else:
119                 return (dic[self.key] > self.value)
120         elif self.op == ge:
121             if isinstance(self.value, StringTypes):
122                 # prefix match
123                 return dic[self.key] == self.value or self.value.startswith('%s.' % dic[self.key])
124             else:
125                 return (dic[self.key] >= self.value)
126         elif self.op == and_:
127             return (dic[self.key] & self.value) # array ?
128         elif self.op == or_:
129             return (dic[self.key] | self.value) # array ?
130         elif self.op == contains:
131             method, subfield = self.key.split('.', 1)
132             return not not [ x for x in dic[method] if x[subfield] == self.value] 
133         else:
134             raise Exception, "Unexpected table format: %r", dic
135
136     def filter(self, dic):
137         """
138         Filter dic according to the current predicate.
139         """
140
141         if '.' in self.key:
142             # users.hrn
143             method, subfield = self.key.split('.', 1)
144             if not method in dic:
145                 return None # XXX
146
147             if isinstance(dic[method], dict):
148                 # We have a 1..1 relationship: apply the same filter to the dict
149                 subpred = Predicate(subfield, self.op, self.value)
150                 match = subpred.match(dic[method])
151                 return dic if match else None
152
153             elif isinstance(dic[method], (list, tuple)):
154                 # 1..N relationships
155                 match = False
156                 if self.op == contains:
157                     return dic if self.match(dic) else None
158                 else:
159                     subpred = Predicate(subfield, self.op, self.value)
160                     dic[method] = subpred.filter(dic[method])
161                     return dic
162             else:
163                 raise Exception, "Unexpected table format: %r", dic
164
165
166         else:
167             # Individual field operations: this could be simplified, since we are now using operators_short !!
168             # XXX match
169             print "current predicate", self
170             print "matching", dic
171             print "----"
172             return dic if self.match(dic) else None
173