aa09379cb5b38f969847b0d2da14edfed842f8b9
[myslice.git] / manifold / util / predicate.py
1 from types import StringTypes
2
3 from operator import (
4     and_, or_, inv, add, mul, sub, mod, truediv, lt, le, ne, gt, ge, eq, neg
5     )
6
7 # Define the inclusion operators
8 class contains(type): pass
9 class included(type): pass
10
11
12 # New modifier: { contains 
13 class Predicate:
14
15     operators = {
16         '=='       : eq,
17         '!='       : ne,
18         '<'        : lt,
19         '<='       : le,
20         '>'        : gt,
21         '>='       : ge,
22         '&&'       : and_,
23         '||'       : or_,
24         'CONTAINS' : contains,
25         'INCLUDED' : included
26     }
27
28     operators_short = {
29         '=' : eq,
30         '~' : ne,
31         '<' : lt,
32         '[' : le,
33         '>' : gt,
34         ']' : ge,
35         '&' : and_,
36         '|' : or_,
37         '}' : contains,
38         '{' : included
39     }
40
41     def __init__(self, *args, **kwargs):
42         """
43         \brief Build a predicate (left, operator, right)
44         \param You can pass:
45             - three args (left, operator, right)
46             - one argument (list or tuple) containing three elements (variable, operator, value)
47             "operator" is a String defined in operators or in operators_short and refers
48                 tMao a binary operation.
49             "left" and "right" refers to a variable/constant involved in the Predicate.
50         """
51         if len(args) == 3:
52             key, op, value = args
53         elif len(args) == 1 and isinstance(args[0], (tuple,list)) and len(args[0]) == 3:
54             key, op, value = args[0]
55         elif len(args) == 1 and isinstance(args[0], Predicate):
56             key, op, value = args[0].get_tuple()
57         else:
58             raise Exception, "Bad initializer for Predicate (args=%r)" % args
59
60         assert not isinstance(value, (frozenset, dict, set)), "Invalid value type (the only valid containers are tuples and lists) (type = %r)" % type(value)
61         if isinstance(value, list):
62             value = tuple(value)
63
64         self.key = key
65         if isinstance(op, StringTypes):
66             op = op.upper()
67         if op in self.operators.keys():
68             self.op = self.operators[op]
69         elif op in self.operators_short.keys():
70             self.op = self.operators_short[op]
71         else:
72             self.op = op
73
74         if isinstance(value, list):
75             self.value = tuple(value)
76         else:
77             self.value = value
78
79     def __str__(self):
80         key, op, value = self.get_str_tuple()
81         if isinstance(value, (tuple, list, set, frozenset)):
82             value = [repr(v) for v in value]
83             value = "[%s]" % ", ".join(value)
84         return "%s %s %r" % (key, op, value) 
85
86     def __repr__(self):
87         return "Predicate<%s %s %r>" % self.get_str_tuple()
88
89     def __hash__(self):
90         return hash(self.get_tuple())
91
92     def __eq__(self, predicate):
93         if not predicate:
94             return False
95         return self.get_tuple() == predicate.get_tuple()
96
97     def get_key(self):
98         return self.key
99     
100     def set_key(self, key):
101         self.key = key
102
103     def get_op(self):
104         return self.op
105
106     def get_value(self):
107         return self.value
108
109     def set_value(self, value):
110         self.value = value
111
112     def get_tuple(self):
113         return (self.key, self.op, self.value)
114
115     def get_str_op(self):
116         op_str = [s for s, op in self.operators.iteritems() if op == self.op]
117         return op_str[0]
118
119     def get_str_tuple(self):
120         return (self.key, self.get_str_op(), self.value,)
121
122     def match(self, dic, ignore_missing=False):
123         if isinstance(self.key, tuple):
124             print "PREDICATE MATCH", self.key
125             print dic
126             print "-----------------------------"
127         
128         # Can we match ?
129         if self.key not in dic:
130             return ignore_missing
131
132         if self.op == eq:
133             if isinstance(self.value, list):
134                 return (dic[self.key] in self.value) # array ?
135             else:
136                 return (dic[self.key] == self.value)
137         elif self.op == ne:
138             if isinstance(self.value, list):
139                 return (dic[self.key] not in self.value) # array ?
140             else:
141                 return (dic[self.key] != self.value) # array ?
142         elif self.op == lt:
143             if isinstance(self.value, StringTypes):
144                 # prefix match
145                 return dic[self.key].startswith('%s.' % self.value)
146             else:
147                 return (dic[self.key] < self.value)
148         elif self.op == le:
149             if isinstance(self.value, StringTypes):
150                 return dic[self.key] == self.value or dic[self.key].startswith('%s.' % self.value)
151             else:
152                 return (dic[self.key] <= self.value)
153         elif self.op == gt:
154             if isinstance(self.value, StringTypes):
155                 # prefix match
156                 return self.value.startswith('%s.' % dic[self.key])
157             else:
158                 return (dic[self.key] > self.value)
159         elif self.op == ge:
160             if isinstance(self.value, StringTypes):
161                 # prefix match
162                 return dic[self.key] == self.value or self.value.startswith('%s.' % dic[self.key])
163             else:
164                 return (dic[self.key] >= self.value)
165         elif self.op == and_:
166             return (dic[self.key] & self.value) # array ?
167         elif self.op == or_:
168             return (dic[self.key] | self.value) # array ?
169         elif self.op == contains:
170             method, subfield = self.key.split('.', 1)
171             return not not [ x for x in dic[method] if x[subfield] == self.value] 
172         elif self.op == included:
173             return dic[self.key] in self.value
174         else:
175             raise Exception, "Unexpected table format: %r" % dic
176
177     def filter(self, dic):
178         """
179         Filter dic according to the current predicate.
180         """
181
182         if '.' in self.key:
183             # users.hrn
184             method, subfield = self.key.split('.', 1)
185             if not method in dic:
186                 return None # XXX
187
188             if isinstance(dic[method], dict):
189                 # We have a 1..1 relationship: apply the same filter to the dict
190                 subpred = Predicate(subfield, self.op, self.value)
191                 match = subpred.match(dic[method])
192                 return dic if match else None
193
194             elif isinstance(dic[method], (list, tuple)):
195                 # 1..N relationships
196                 match = False
197                 if self.op == contains:
198                     return dic if self.match(dic) else None
199                 else:
200                     subpred = Predicate(subfield, self.op, self.value)
201                     dic[method] = subpred.filter(dic[method])
202                     return dic
203             else:
204                 raise Exception, "Unexpected table format: %r", dic
205
206
207         else:
208             # Individual field operations: this could be simplified, since we are now using operators_short !!
209             # XXX match
210             print "current predicate", self
211             print "matching", dic
212             print "----"
213             return dic if self.match(dic) else None
214
215     def get_field_names(self):
216         if isinstance(self.key, (list, tuple, set, frozenset)):
217             return set(self.key)
218         else:
219             return set([self.key])
220
221     def get_value_names(self):
222         if isinstance(self.value, (list, tuple, set, frozenset)):
223             return set(self.value)
224         else:
225             return set([self.value])