2
# This file is part of GNU Enterprise.
4
# GNU Enterprise is free software; you can redistribute it
5
# and/or modify it under the terms of the GNU General Public
6
# License as published by the Free Software Foundation; either
7
# version 2, or (at your option) any later version.
9
# GNU Enterprise is distributed in the hope that it will be
10
# useful, but WITHOUT ANY WARRANTY; without even the implied
11
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12
# PURPOSE. See the GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public
15
# License along with program; see the file COPYING. If not,
16
# write to the Free Software Foundation, Inc., 59 Temple Place
17
# - Suite 330, Boston, MA 02111-1307, USA.
19
# Copyright 2000-2005 Free Software Foundation
21
# $Id: GConditions.py 7057 2005-02-23 16:53:58Z johannes $
24
from gnue.common.definitions.GObjects import GObj
25
from gnue.common.formatting import GTypecast
32
class ConditionError (gException):
35
class ConditionNotSupported (ConditionError):
38
class MalformedConditionTreeError (ConditionError):
41
class ArgumentCountError (MalformedConditionTreeError):
42
def __init__ (self, element, wanted):
43
msg = u_("Conditionelement '%(element)s' was expected to have '%(wanted)d'"
44
"arguments, but only has %(real)d'") \
45
% {'element': element.__class__.split ('.') [-1],
47
'real' : len (element._children)}
48
MalformedConditionTreeError.__init__ (self, msg)
50
class MissingFieldError (ConditionError):
51
def __init__ (self, element):
52
msg = u_("The field '%(field)s' has no entry in the given lookup-table") \
53
% {'field': element.name }
54
ConditionError.__init__ (self, msg)
57
class UnificationError (gException):
60
class ConversionRuleError (UnificationError):
61
def __init__ (self, value1, value2):
62
msg = u_("No unification rule for combination '%(type1)s' and "
64
% {'type1': type (value1).__name__,
65
'type2': type (value2).__name__}
66
UnificationError.__init__ (self, msg)
68
class ConversionError (UnificationError):
69
def __init__ (self, value1, value2):
70
msg = u_("Value '%(value1)s' of type '%(type1)s' cannot be converted "
71
"into type '%(type2)s'") \
73
'type1' : type (value1).__name__,
74
'type2' : type (value2).__name__}
75
UnificationError.__init__ (self, msg)
79
# =============================================================================
80
# Base condition class; this is class is acts as root node of condition trees
81
# =============================================================================
83
class GCondition (GObj):
85
A GCondition instance is allways the root node of a condition tree. All
86
children of a GCondition node are evaluated and combined using an AND
87
condition if not otherwise stated.
89
def __init__(self, parent = None, type = "GCCondition"):
90
GObj.__init__ (self, parent, type = type)
91
self._maxChildren = None
94
# ---------------------------------------------------------------------------
95
# Make sure an element of the tree has the requested number of children
96
# ---------------------------------------------------------------------------
98
def _needChildren (self, number):
100
This function verifies if a condition element has a given number of
101
children. If not an ArgumentCountError will be raised.
103
if number is not None and len (self._children) != number:
104
raise ArgumentCountError, (self, number)
107
# ---------------------------------------------------------------------------
108
# Evaluate a condition tree using the given lookup dictionary
109
# ---------------------------------------------------------------------------
111
def evaluate (self, lookup):
113
This function evaluates the condition tree using the dictionary @lookup for
114
retrieving field values. All children must evaluate to TRUE; evaluation
115
stops on the first false result.
119
for child in self._children:
120
if not child.evaluate (lookup):
126
# ---------------------------------------------------------------------------
127
# Validate an element of a condition tree
128
# ---------------------------------------------------------------------------
132
This function calls validate () on all it's children. Descendants might
133
override this function to do integrity checks and things like that.
135
self._needChildren (self._maxChildren)
137
for child in self._children:
141
# ---------------------------------------------------------------------------
142
# Convert an element into prefix notation
143
# ---------------------------------------------------------------------------
145
def prefixNotation (self):
147
This function returns the prefix notation of an element and all it's
151
append = result.extend
153
if isinstance (self, GConditionElement):
154
result.append (self._type [2:])
155
append = result.append
157
for child in self._children:
158
append (child.prefixNotation ())
163
# ---------------------------------------------------------------------------
164
# Build an element and all it's children from a prefix notation list
165
# ---------------------------------------------------------------------------
167
def buildFromList (self, prefixList):
169
This function creates a (partial) condition tree from a prefix notation
172
checktype (prefixList, types.ListType)
175
item = prefixList [0]
177
# be nice if there's a condition part missing
179
if isinstance (item, types.ListType):
180
self.buildFromList (item)
183
# automatically map 'field' to 'Field' and 'const' to 'Const'
184
if item in ['field', 'const']:
187
element = getattr (sys.modules [__name__], "GC%s" % item) (self)
190
(table, masterlink, detaillink) = prefixList [1:4]
191
element.table = table
192
element.masterlink = masterlink
193
element.detaillink = detaillink
196
for subitem in prefixList [offset:]:
197
element.buildFromList (subitem)
200
# ---------------------------------------------------------------------------
201
# Break up all top-down references
202
# ---------------------------------------------------------------------------
204
def breakReferences (self):
206
This function resolves the reference to the parent instance avoiding
211
for item in self._children:
212
item.breakReferences ()
215
# =============================================================================
217
# =============================================================================
219
class GConditions(GCondition):
220
def __init__(self, parent=None, type="GCConditions"):
221
GCondition.__init__(self, parent, type = type)
223
class GConditionElement (GCondition) :
224
def __init__(self, parent=None, type="GConditionElement"):
225
GCondition.__init__ (self, parent, type = type)
228
# -----------------------------------------------------------------------------
229
# A Field element in the condition tree
230
# -----------------------------------------------------------------------------
232
class GCField (GConditionElement):
233
def __init__(self, parent, name = None, datatype = "char"):
234
GConditionElement.__init__ (self, parent, 'GCCField')
238
# ---------------------------------------------------------------------------
239
# Evaluate a field element
240
# ---------------------------------------------------------------------------
242
def evaluate (self, lookup):
244
This function returns the fields value in the given lookup dictionary. If
245
this dictionary has no key for the field a MissingFieldError will be
248
if not lookup.has_key (self.name):
249
raise MissingFieldError, (self.name)
251
return lookup [self.name]
254
# ---------------------------------------------------------------------------
255
# A field in prefix notation is a tuple of 'field' and fieldname
256
# ---------------------------------------------------------------------------
258
def prefixNotation (self):
260
The prefix notation of a field element is a tuple of the identifier 'field'
261
(acting as operator) and the field's name.
263
return ['Field', self.name]
266
# ---------------------------------------------------------------------------
267
# to complete a field element from a prefix notation set the fieldname
268
# ---------------------------------------------------------------------------
270
def buildFromList (self, prefixList):
272
The single argument to a field 'operator' could be it's name, so this
273
method set's the fieldname.
275
checktype (prefixList, [types.StringType, types.UnicodeType])
276
self.name = prefixList
279
# -----------------------------------------------------------------------------
280
# A constant definition in a condition tree
281
# -----------------------------------------------------------------------------
283
class GCConst (GConditionElement):
284
def __init__ (self, parent, value = None, datatype = "char"):
285
GConditionElement.__init__ (self, parent, 'GCCConst')
290
# ---------------------------------------------------------------------------
291
# Evaluate a constant
292
# ---------------------------------------------------------------------------
294
def evaluate (self, lookup):
296
This function returns the constants value
301
# ---------------------------------------------------------------------------
302
# The prefix notation of a constant is a tuple of identifier and value
303
# ---------------------------------------------------------------------------
305
def prefixNotation (self):
307
The prefix notation of a constant is a tuple of the identifier 'Const' and
308
the constant's value.
310
return ['Const', self.value]
313
# ---------------------------------------------------------------------------
314
# Recreate a constant from a prefix notation
315
# ---------------------------------------------------------------------------
317
def buildFromList (self, prefixList):
319
The single argument of a constant 'operator' could be it's value, so this
320
function set the constant's value.
322
self.value = prefixList
325
# -----------------------------------------------------------------------------
326
# Base class for parameter elements in a condition tree
327
# -----------------------------------------------------------------------------
329
class GCParam (GConditionElement):
330
def __init__ (self, parent, name = None, datatype = "char"):
331
GConditionElement.__init__ (self, parent, 'GCCParam')
335
# ---------------------------------------------------------------------------
336
# Return the value of a parameter
337
# ---------------------------------------------------------------------------
341
Descendants override this function to return the value of the parameter.
346
# ---------------------------------------------------------------------------
347
# Evaluate the parameter object
348
# ---------------------------------------------------------------------------
350
def evaluate (self, lookup):
352
A parameter element evaluates to it's value.
354
return self.getValue ()
357
# ---------------------------------------------------------------------------
358
# Return a parameter object in prefix notation
359
# ---------------------------------------------------------------------------
361
def prefixNotation (self):
363
The prefix notation of a parameter object is a 'constant' with the
366
return ['Const', self.getValue ()]
369
# =============================================================================
370
# Base classes for unary and binary operations
371
# =============================================================================
373
class GUnaryConditionElement (GConditionElement):
374
def __init__ (self, parent = None, elementType = ''):
375
self._maxChildren = 1
376
GConditionElement.__init__ (self, parent, elementType)
378
class GBinaryConditionElement (GConditionElement):
379
def __init__ (self, parent = None, elementType = ''):
380
GConditionElement.__init__ (self, parent, elementType)
381
self._maxChildren = 2
384
# ---------------------------------------------------------------------------
385
# Evaluating a binary element means evaluation of both children
386
# ---------------------------------------------------------------------------
388
def evaluate (self, lookup):
390
This function evaluates both children of a binary element storing their
391
values in the property 'values'. Descendants can use these values for
394
self._needChildren (self._maxChildren)
395
self.values = unify ([child.evaluate (lookup) for child in self._children])
398
# =============================================================================
400
# =============================================================================
402
# -----------------------------------------------------------------------------
403
# n-ary operation: AND
404
# -----------------------------------------------------------------------------
406
class GCand (GConditionElement):
407
def __init__ (self, parent = None):
408
GConditionElement.__init__ (self, parent, 'GCand')
411
# -----------------------------------------------------------------------------
412
# n-ary operation: OR
413
# -----------------------------------------------------------------------------
415
class GCor (GConditionElement):
416
def __init__ (self, parent = None):
417
GConditionElement.__init__ (self, parent, 'GCor')
419
# ---------------------------------------------------------------------------
420
# Evaluate an OR tree
421
# ---------------------------------------------------------------------------
423
def evaluate (self, lookup):
425
This function concatenates all children of this element by a logical OR.
426
The iteration stops on the first 'true' result.
428
for child in self._children:
429
if child.evaluate (lookup):
434
# -----------------------------------------------------------------------------
435
# unary operation: NOT
436
# -----------------------------------------------------------------------------
438
class GCnot (GUnaryConditionElement):
439
def __init__ (self, parent = None):
440
GUnaryConditionElement.__init__ (self, parent, 'GCnot')
442
# ---------------------------------------------------------------------------
443
# logically Invert the childs result
444
# ---------------------------------------------------------------------------
446
def evaluate (self, lookup):
448
This function logically inverts the child's evaluation
450
self._needChildren (self._maxChildren)
451
return not self._children [0].evaluate (lookup)
455
# =============================================================================
457
# =============================================================================
459
# ---------------------------------------------------------------------------
460
# n-ary operation: Addition
461
# ---------------------------------------------------------------------------
463
class GCadd (GConditionElement):
464
def __init__ (self, parent = None):
465
GConditionElement.__init__ (self, parent, 'GCadd')
467
# ---------------------------------------------------------------------------
468
# Evaluate the addition element
469
# ---------------------------------------------------------------------------
470
def evaluate (self, lookup):
472
This function creates the sum of all it's children. A unify is used to
473
ensure all children evaluate to a numeric type.
476
for child in self._children:
477
result += unify ([child.evaluation (lookup), 0]) [0]
481
# -----------------------------------------------------------------------------
482
# n-ary operation: Subtraction
483
# -----------------------------------------------------------------------------
485
class GCsub (GConditionElement):
486
def __init__ (self, parent = None):
487
GConditionElement.__init__ (self, parent, 'GCsub')
489
# ---------------------------------------------------------------------------
490
# Evaluate the subtraction element
491
# ---------------------------------------------------------------------------
493
def evaluate (self, lookup):
496
for child in self._children:
497
value = unify ([child.evaluation (lookup), 0]) [0]
506
# -----------------------------------------------------------------------------
507
# n-ary operation: Multiplication
508
# -----------------------------------------------------------------------------
510
class GCmul (GConditionElement):
511
def __init__ (self, parent = None):
512
GConditionElement.__init__ (self, parent, 'GCmul')
514
# ---------------------------------------------------------------------------
515
# Evaluate the multiplication
516
# ---------------------------------------------------------------------------
518
def evaluate (self, lookup):
521
for child in self._children:
522
value = unify ([child.evaluate (lookup), 0]) [0]
531
# -----------------------------------------------------------------------------
532
# n-ary operation: Division
533
# -----------------------------------------------------------------------------
535
class GCdiv (GConditionElement):
536
def __init__ (self, parent = None):
537
GConditionElement.__init__ (self, parent, 'GCdiv')
539
# ---------------------------------------------------------------------------
540
# Evaluate the division element
541
# ---------------------------------------------------------------------------
543
def evaluate (self, lookup):
546
for child in self._children:
547
value = unify ([child.evaluate (lookup), 0]) [0]
555
# -----------------------------------------------------------------------------
556
# unary operation: numeric negation
557
# -----------------------------------------------------------------------------
559
class GCnegate (GUnaryConditionElement):
560
def __init__ (self, parent = None):
561
GUnaryConditionElement.__init__ (self, parent, 'GCnegate')
563
# ---------------------------------------------------------------------------
564
# Evaluation of the numeric negation
565
# ---------------------------------------------------------------------------
567
def evaluate (self, lookup):
569
This function does a numeric negation on the child's evaluation result.
571
self._needChildren (self._maxChildren)
572
return -unify ([self._children [0].evaluate (lookup), 0]) [0]
575
# =============================================================================
576
# Relational operations
577
# =============================================================================
579
# -----------------------------------------------------------------------------
581
# -----------------------------------------------------------------------------
583
class GCeq (GBinaryConditionElement):
584
def __init__ (self, parent = None):
585
GBinaryConditionElement.__init__ (self, parent, 'GCeq')
587
# ---------------------------------------------------------------------------
588
# evaluate EQ relation
589
# ---------------------------------------------------------------------------
591
def evaluate (self, lookup):
592
GBinaryConditionElement.evaluate (self, lookup)
593
return self.values [0] == self.values [1]
596
# -----------------------------------------------------------------------------
598
# -----------------------------------------------------------------------------
600
class GCne (GBinaryConditionElement):
601
def __init__ (self, parent = None):
602
GBinaryConditionElement.__init__ (self, parent, 'GCne')
604
# ---------------------------------------------------------------------------
605
# evaluate NE relation
606
# ---------------------------------------------------------------------------
608
def evaluate (self, lookup):
609
GBinaryConditionElement.evaluate (self, lookup)
610
return self.values [0] != self.values [1]
613
# -----------------------------------------------------------------------------
615
# -----------------------------------------------------------------------------
617
class GCgt (GBinaryConditionElement):
618
def __init__ (self, parent = None):
619
GBinaryConditionElement.__init__ (self, parent, 'GCgt')
621
# ---------------------------------------------------------------------------
622
# evaluate GT relation
623
# ---------------------------------------------------------------------------
625
def evaluate (self, lookup):
626
GBinaryConditionElement.evaluate (self, lookup)
627
return self.values [0] > self.values [1]
630
# -----------------------------------------------------------------------------
632
# -----------------------------------------------------------------------------
634
class GCge (GBinaryConditionElement):
635
def __init__ (self, parent = None):
636
GBinaryConditionElement.__init__ (self, parent, 'GCge')
638
# ---------------------------------------------------------------------------
639
# evaluate GE relation
640
# ---------------------------------------------------------------------------
642
def evaluate (self, lookup):
643
GBinaryConditionElement.evaluate (self, lookup)
644
return self.values [0] >= self.values [1]
648
# -----------------------------------------------------------------------------
650
# -----------------------------------------------------------------------------
652
class GClt (GBinaryConditionElement):
653
def __init__ (self, parent = None):
654
GBinaryConditionElement.__init__ (self, parent, 'GClt')
656
# ---------------------------------------------------------------------------
657
# evaluate LT relation
658
# ---------------------------------------------------------------------------
660
def evaluate (self, lookup):
661
GBinaryConditionElement.evaluate (self, lookup)
662
return self.values [0] < self.values [1]
665
# -----------------------------------------------------------------------------
667
# -----------------------------------------------------------------------------
669
class GCle (GBinaryConditionElement):
670
def __init__ (self, parent = None):
671
GBinaryConditionElement.__init__ (self, parent, 'GCle')
673
# ---------------------------------------------------------------------------
674
# evaluate LE relation
675
# ---------------------------------------------------------------------------
677
def evaluate (self, lookup):
678
GBinaryConditionElement.evaluate (self, lookup)
679
return self.values [0] <= self.values [1]
682
# -----------------------------------------------------------------------------
684
# -----------------------------------------------------------------------------
686
class GClike (GBinaryConditionElement):
687
def __init__ (self, parent = None):
688
GBinaryConditionElement.__init__ (self, parent, 'GClike')
690
# ---------------------------------------------------------------------------
691
# Evaluate a like condition
692
# ---------------------------------------------------------------------------
694
def evaluate (self, lookup):
695
GBinaryConditionElement.evaluate (self, lookup)
696
# None cannot be like something else. You should use 'NULL' or 'NOT NULL'
698
if self.values [0] is None:
701
strpat = "^%s" % self.values [1]
702
strpat = strpat.replace ('?', '.').replace ('%', '.*')
703
pattern = re.compile (strpat)
704
return pattern.match (self.values [0]) is not None
707
# -----------------------------------------------------------------------------
709
# -----------------------------------------------------------------------------
711
class GCnotlike (GBinaryConditionElement):
712
def __init__ (self, parent = None):
713
GBinaryConditionElement.__init__ (self, parent, 'GCnotlike')
715
# ---------------------------------------------------------------------------
716
# Evaluate an inverted like condition
717
# ---------------------------------------------------------------------------
719
def evaluate (self, lookup):
720
GBinaryConditionElement.evaluate (self, lookup)
721
strpat = "^%s" % self.values [1]
722
strpat = strpat.replace ('?', '.').replace ('%', '.*')
723
pattern = re.compile (strpat)
724
return pattern.match (self.values [0]) is None
727
# -----------------------------------------------------------------------------
729
# -----------------------------------------------------------------------------
731
class GCbetween (GConditionElement):
732
def __init__ (self, parent = None):
733
self._maxChildren = 3
734
GConditionElement.__init__ (self, parent, 'GCbetween')
736
# ---------------------------------------------------------------------------
737
# evaluate beetween relation
738
# ---------------------------------------------------------------------------
740
def evaluate (self, lookup):
741
self._needChildren (self._maxChildren)
742
values = unify ([v.evaluate (lookup) for v in self._children])
743
return values [1] <= values [0] <= values [2]
746
# -----------------------------------------------------------------------------
748
# -----------------------------------------------------------------------------
750
class GCnotbetween (GConditionElement):
751
def __init__ (self, parent = None):
752
self._maxChildren = 3
753
GConditionElement.__init__ (self, parent, 'GCnotbetween')
755
# ---------------------------------------------------------------------------
756
# evaluate an inverted beetween relation
757
# ---------------------------------------------------------------------------
759
def evaluate (self, lookup):
760
self._needChildren (self._maxChildren)
761
values = unify ([v.evaluate (lookup) for v in self._children])
762
return not (values [1] <= values [0] <= values [2])
765
# -----------------------------------------------------------------------------
767
# -----------------------------------------------------------------------------
769
class GCnull (GUnaryConditionElement):
770
def __init__ (self, parent = None):
771
GUnaryConditionElement.__init__ (self, parent, 'GCnull')
773
# ---------------------------------------------------------------------------
774
# evaluate if a child is NULL
775
# ---------------------------------------------------------------------------
777
def evaluate (self, lookup):
778
self._needChildren (self._maxChildren)
779
return self._children [0].evaluate (lookup) is None
782
# -----------------------------------------------------------------------------
784
# -----------------------------------------------------------------------------
786
class GCnotnull (GUnaryConditionElement):
787
def __init__ (self, parent = None):
788
GUnaryConditionElement.__init__ (self, parent, 'GCnotnull')
790
# ---------------------------------------------------------------------------
791
# evaluate if a child is not NULL
792
# ---------------------------------------------------------------------------
794
def evaluate (self, lookup):
795
self._needChildren (self._maxChildren)
796
return self._children [0].evaluate (lookup) is not None
798
# -----------------------------------------------------------------------------
800
# -----------------------------------------------------------------------------
802
class GCupper (GUnaryConditionElement):
803
def __init__ (self, parent = None):
804
GUnaryConditionElement.__init__ (self, parent, 'GCupper')
806
# ---------------------------------------------------------------------------
808
# ---------------------------------------------------------------------------
810
def evaluate (self, lookup):
811
self._needChildren (self._maxChildren)
812
return string.upper (self._children [0].evaluate (lookup))
814
# -----------------------------------------------------------------------------
816
# -----------------------------------------------------------------------------
818
class GClower (GUnaryConditionElement):
819
def __init__ (self, parent = None):
820
GUnaryConditionElement.__init__ (self, parent, 'GClower')
822
# ---------------------------------------------------------------------------
824
# ---------------------------------------------------------------------------
826
def evaluate (self, lookup):
827
self._needChildren (self._maxChildren)
828
return string.lower (self._children [0].evaluate (lookup))
831
# -----------------------------------------------------------------------------
833
# -----------------------------------------------------------------------------
835
class GCexist (GConditionElement):
836
def __init__ (self, parent = None):
837
GConditionElement.__init__ (self, parent, 'GCexist')
840
# ---------------------------------------------------------------------------
841
# Evaluate an exist-condition
842
# ---------------------------------------------------------------------------
844
def evaluate (self, lookup):
846
if self.callback is None:
847
raise NotImplementedError
849
return self.callback (self, lookup)
852
# ---------------------------------------------------------------------------
853
# Convert an element into prefix notation
854
# ---------------------------------------------------------------------------
856
def prefixNotation (self):
858
This function returns the prefix notation of an exist element and all it's
861
result = ['exist', self.table, self.masterlink, self.detaillink]
863
for child in self._children:
864
result.append (child.prefixNotation ())
870
# =============================================================================
871
# Return a dictionary of all XML elements available
872
# =============================================================================
874
def getXMLelements (updates = {}):
877
'BaseClass': GCondition,
878
'ParentTags': ('conditions','and','or','not','negate'),
879
'Deprecated': 'Use the <condition> tag instead.',
882
'BaseClass': GCondition,
883
'ParentTags': ('conditions','and','or','not','negate','exist') },
885
'BaseClass': GCField,
886
'Description': 'Defines a database table\'s field in a condition.',
890
'Typecast': GTypecast.name } },
891
'ParentTags': ('eq','ne','lt','le','gt','ge','add','sub','mul',
892
'div','like','notlike','between','notbetween',
893
'upper', 'lower', 'null', 'notnull') },
895
'BaseClass': GCParam,
896
'Description': 'Defines a parameter value in a condition.',
901
'Typecast': GTypecast.name } },
902
'ParentTags': ('eq','ne','lt','le','gt','ge','add','sub','mul',
903
'div','like','notlike','between','notbetween') },
905
'BaseClass': GCConst,
906
'Description': 'Defines a constant value in a condition.',
910
'Typecast': GTypecast.text },
912
'Typecast': GTypecast.text } },
913
'ParentTags': ('eq','ne','lt','le','gt','ge','add','sub','mul',
914
'div','like','notlike','between','notbetween') },
917
'Description': 'Implements addition.',
918
'ParentTags': ('eq','ne','lt','le','gt','ge','add','sub','mul',
919
'div','like','notlike','between','notbetween') },
922
'Description': 'Implements subtraction.',
923
'ParentTags': ('eq','ne','lt','le','gt','ge','add','sub','mul',
924
'div','like','notlike','between','notbetween') },
927
'Description': 'Implements multiplication.',
928
'ParentTags': ('eq','ne','lt','le','gt','ge','add','sub','mul',
929
'div','like','notlike','between','notbetween') },
932
'Description': 'Implements division.',
933
'ParentTags': ('eq','ne','lt','le','gt','ge','add','sub','mul',
934
'div','like','notlike','between','notbetween') },
937
'Description': 'Implements logical AND relation.',
938
'ParentTags': ('condition','and','or','not','negate') },
941
'Description': 'Implements logical OR relation.',
942
'ParentTags': ('condition','and','or','not','negate') },
945
'Description': 'Implements logical NOT relation.',
946
'ParentTags': ('condition','and','or','not','negate') },
948
'BaseClass': GCnegate,
949
'Description': 'Implements numerical negation.',
950
'ParentTags': ('condition','and','or','not','negate') },
953
'Description': 'Implements a {field} = {value} condition.',
954
'ParentTags': ('condition','and','or','not','negate') },
957
'Description': 'Implements a {field} <> {value} condition.',
958
'ParentTags': ('condition','and','or','not','negate') },
961
'Description': 'Implements a {field} > {value} condition.',
962
'ParentTags': ('condition','and','or','not','negate') },
965
'Description': 'Implements a {field} >= {value} condition.',
966
'ParentTags': ('condition','and','or','not','negate') },
969
'Description': 'Implements a {field} < {value} condition.',
970
'ParentTags': ('condition','and','or','not','negate') },
973
'Description': 'Implements a {field} <= {value} condition.',
974
'ParentTags': ('condition','and','or','not','negate') },
977
'Description': 'Implements a {field} LIKE {value} condition.',
978
'ParentTags': ('condition','and','or','not','negate') },
980
'BaseClass': GCnotlike,
981
'Description': 'Implements a {field} NOT LIKE {value} condition.',
982
'ParentTags': ('condition','and','or','not','negate') },
984
'BaseClass': GCbetween,
985
'Description': 'Implements a {field} BETWEEN {value1} {value2} '
987
'ParentTags': ('condition','and','or','not','negate') },
989
'BaseClass': GCnotbetween,
990
'Description': 'Implements a {field} NOT BETWEEN {value1} {value2} '
992
'ParentTags': ('condition','and','or','not','negate') },
995
'Description': 'Implements a {field} IS NULL condition.',
996
'ParentTags': ('condition','and','or','not') },
998
'BaseClass': GCnotnull,
999
'Description': 'Implements a {field} IS NOT NULL condition.',
1000
'ParentTags': ('condition','and','or','not') },
1002
'BaseClass': GCupper,
1003
'Description': 'Implements upper({value}).',
1004
'ParentTags': ('eq','ne','lt','le','gt','ge',
1005
'like','notlike','between','notbetween') },
1007
'BaseClass': GClower,
1008
'Description': 'Implements lower({value}).',
1009
'ParentTags': ('eq','ne','lt','le','gt','ge',
1010
'like','notlike','between','notbetween') },
1012
'BaseClass': GCexist,
1013
'Description': 'Implements an exist condition.',
1017
'Description': 'TODO',
1018
'Typecast': GTypecast.name },
1021
'Description': 'TODO',
1022
'Typecast': GTypecast.text},
1025
'Description': 'TODO',
1026
'Typecast': GTypecast.text}},
1027
'ParentTags': ('eq','ne','lt','le','gt','ge',
1028
'like','notlike','between','notbetween') },
1031
for alteration in updates.keys():
1032
xmlElements[alteration].update(updates[alteration])
1036
###############################################################################
1037
###############################################################################
1038
#### Convenience Methods ##
1039
###############################################################################
1040
###############################################################################
1042
# =============================================================================
1043
# Create a condition tree from an element sequence in prefix notation
1044
# =============================================================================
1046
def buildTreeFromList (prefixList):
1048
This function creates a new condition tree from the given element sequence,
1049
which must be in prefix notation.
1051
checktype (prefixList, types.ListType)
1053
result = GCondition ()
1054
result.buildFromList (prefixList)
1058
# =============================================================================
1059
# Create a condition tree from a dictionary
1060
# =============================================================================
1062
def buildConditionFromDict (dictionary, comparison = GCeq, logic = GCand):
1064
This function creates a new condition tree using @comparison as operation
1065
between keys and values and @logic as concatenation for all keys.
1067
result = GCondition ()
1070
if len (dictionary.keys ()):
1071
lastParent = logic (lastParent)
1073
for key, value in dictionary.items ():
1074
operation = comparison (lastParent)
1075
GCField (operation, key)
1077
if type (value) in [types.IntType, types.FloatType, types.LongType]:
1078
consttype = 'number'
1082
GCConst (operation, value, consttype)
1087
# =============================================================================
1088
# Combine two conditions with an AND clause. Side-effect: cond1 will be changed
1089
# =============================================================================
1091
def combineConditions (cond1, cond2):
1093
This function combines the two condition trees @cond1 and @cond2 using an AND
1094
clause. Both arguments can be given as conditiontrees or as dictionaries. In
1095
the latter case they would be converted using buildConditionFromDict (). As a
1096
side effect of this function, @cond1 would be changed (if neccessary). If
1097
neither of the conditions has a single top-level element of type GCand a new
1098
element would be created.
1100
# check for the trivial cases
1101
if cond1 is None or cond1 == {}:
1104
elif cond2 is None or cond2 == {}:
1107
# make sure both parts are condition trees
1108
if isinstance (cond1, types.DictType):
1109
cond1 = buildConditionFromDict (cond1)
1111
if isinstance (cond2, types.DictType):
1112
cond2 = buildConditionFromDict (cond2)
1114
# if the master condition has no elements, assign the second condition's
1116
if not len (cond1._children):
1117
cond1._children = cond2._children
1120
elif len (cond2._children):
1121
# First normalize the master condition. This means we make sure to have a
1122
# valid logical operator as single top element
1123
if len (cond1._children) > 1 or not isinstance (cond1._children [0], GCand):
1124
oldChildren = cond1._children [:]
1125
cond1._children = []
1126
parent = GCand (cond1)
1127
parent._children = oldChildren
1130
parent = cond1._children [0]
1132
# determine where to start in the second condition tree
1134
while len (buddy._children) == 1 and \
1135
isinstance (buddy._children [0], GCand):
1136
buddy = buddy._children [0]
1138
# and append all of the buddy's children to the top-level-and
1139
parent._children.extend (buddy._children)
1145
if update is not None:
1146
for child in update._children:
1147
child._parent = update
1152
# =============================================================================
1153
# Unify all elements in values to the same type
1154
# =============================================================================
1158
This function converts all values in the sequence @values to the same types
1159
pushing the results into the sequence @result.
1162
__unify (values, result)
1165
# -----------------------------------------------------------------------------
1166
# Actual working method for unification
1167
# -----------------------------------------------------------------------------
1168
def __unify (values, result):
1170
This function does the dirty work of 'unify ()'.
1173
checktype (values, types.ListType)
1175
if not len (values):
1178
elif len (values) == 1:
1179
result.append (values [0])
1182
if isinstance (values [0], types.StringType):
1183
values [0] = unicode (values [0])
1184
if isinstance (values [1], types.StringType):
1185
values [1] = unicode (values [1])
1190
if v1 is None or v2 is None:
1191
result.append (None)
1192
values.remove (None)
1193
__unify (values, result)
1195
elif type (v1) == type (v2):
1198
__unify (values, result)
1201
# String-Conversions
1202
if isinstance (v1, types.UnicodeType) or isinstance (v2, types.UnicodeType):
1203
if isinstance (v1, types.UnicodeType):
1211
if hasattr (types, "BooleanType") and \
1212
isinstance (chkValue, types.BooleanType):
1213
if oldValue.upper () in ['TRUE', 'T']:
1215
elif oldValue.upper () in ['FALSE', 'F']:
1218
raise ConversionError, (oldValue, chkValue)
1220
# String to Integer, Long or Float
1221
elif isinstance (chkValue, types.IntType) or \
1222
isinstance (chkValue, types.LongType) or \
1223
isinstance (chkValue, types.FloatType):
1225
if oldValue.upper () in ['TRUE', 'T']:
1227
elif oldValue.upper () in ['FALSE', 'F']:
1230
newValue = int (oldValue)
1235
newValue = float (oldValue)
1238
raise ConversionError, (oldValue, chkValue)
1240
# String to DateTime
1241
elif isinstance (chkValue, mx.DateTime.DateTimeType) or \
1242
isinstance (chkValue, mx.DateTime.DateTimeDeltaType):
1245
newValue = mx.DateTime.Parser.DateTimeFromString (oldValue)
1248
raise ConversionError, (oldValue, chkValue)
1251
raise ConversionRuleError, (oldValue, chkValue)
1253
# Boolean conversions
1254
elif hasattr (types, "BooleanType") and \
1255
(isinstance (v1, types.BooleanType) or \
1256
isinstance (v2, types.BooleanType)):
1257
if isinstance (v1, types.BooleanType):
1264
# Boolean to Integer
1265
if isinstance (chkValue, types.IntType):
1272
elif isinstance (chkValue, types.LongType):
1279
raise ConversionRuleError, (oldValue, chkValue)
1281
# Integer conversions
1282
elif isinstance (v1, types.IntType) or isinstance (v2, types.IntType):
1283
if isinstance (v1, types.IntType):
1291
if isinstance (chkValue, types.FloatType):
1292
newValue = float (oldValue)
1294
elif isinstance (chkValue, types.LongType):
1295
newValue = long (oldValue)
1298
raise ConversionRuleError, (oldValue, chkValue)
1301
elif isinstance (v1, types.LongType) or isinstance (v2, types.LongType):
1302
if isinstance (v1, types.LongType):
1310
if isinstance (chkValue, types.FloatType):
1311
newValue = float (oldValue)
1313
raise ConversionRuleError, (oldValue, chkValue)
1316
raise ConversionRuleError, (v1, v2)
1318
values [oldValue == v2] = newValue
1319
__unify (values, result)
1322
# =============================================================================
1323
# Definition of an impossible condition
1324
# =============================================================================
1326
GCimpossible = buildTreeFromList (['eq', ['const', 1], ['const', 2]])
1330
###############################################################################
1331
###############################################################################
1332
#### Depreciated Methods ##
1333
###############################################################################
1334
###############################################################################
1337
# a table with extra information about the different condition types
1338
# (needed for condition tree <-> prefix table conversion)
1340
conditionElements = {
1341
'add': (2, 999, GCadd ),
1342
'sub': (2, 999, GCsub ),
1343
'mul': (2, 999, GCmul ),
1344
'div': (2, 999, GCdiv ),
1345
'and': (1, 999, GCand ),
1346
'or': (2, 999, GCor ),
1347
'not': (1, 1, GCnot ),
1348
'negate': (1, 1, GCnegate ),
1349
'eq': (2, 2, GCeq ),
1350
'ne': (2, 2, GCne ),
1351
'gt': (2, 2, GCgt ),
1352
'ge': (2, 2, GCge ),
1353
'lt': (2, 2, GClt ),
1354
'le': (2, 2, GCle ),
1355
'like': (2, 2, GClike ),
1356
'notlike': (2, 2, GCnotlike ),
1357
'between': (3, 3, GCbetween ),
1358
'null': (1, 1, GCnull),
1359
'upper': (1, 1, GCupper),
1360
'lower': (1, 1, GClower),
1363
# creates an GCondition Tree out of an list of tokens in a prefix
1366
def buildTreeFromPrefix(term):
1368
# create a GCondition object as top object in the object stack
1369
parent={0:(GCondition())}
1371
# GCondition will have only one parameter
1372
# add paramcount=1 to the parameter count stack
1375
# set start level for stack to zero
1379
# convert type into an object
1380
if conditionElements.has_key(i[0]):
1381
e=conditionElements[i[0]][2](parent[level])
1383
# get parameter count
1384
paramcount[level]=conditionElements[i[0]][0]
1387
e=GCField(parent[level], i[1])
1388
paramcount[level]=paramcount[level]-1
1389
if paramcount[level]==0:
1392
e=GCConst(parent[level], i[1])
1393
paramcount.update({level:(paramcount[level]-1)})
1394
if paramcount[level]==0:
1396
# print "NAME: %s VALUE: %s LEVEL: %s PCOUNT: %s" % \
1397
# (i[0],i[1],level,paramcount[level])
1403
def buildPrefixFromTree(conditionTree):
1404
if type(conditionTree) != types.InstanceType:
1405
tmsg = u_("No valid condition tree")
1406
raise ConditionError, tmsg
1408
otype = string.lower(conditionTree._type[2:])
1411
# care for objects without children
1413
if otype == 'cfield':
1414
return [('field',"%s" % conditionTree.name)]
1416
elif otype == 'cconst':
1417
return [('const',conditionTree.value)]
1419
elif otype == 'cparam':
1420
return [('const', conditionTree.getValue())]
1423
# if its an conditional object, then process it's children
1425
elif conditionElements.has_key(otype):
1428
# first add operator to the list
1429
result.append((otype,'')); # ,None));
1432
# change operations with more than there minimal element no into
1433
# multiple operations with minimal elements
1434
# reason: to prevent a b c d AND OR being not well defined
1435
# because it can be a"a b c d AND AND OR" or "a b c d AND OR OR"
1436
paramcount=len(conditionTree._children)
1437
while (paramcount > \
1438
conditionElements[otype][0]):
1439
paramcount=paramcount-1
1440
result.append((otype,''));
1444
for i in range(0, len(conditionTree._children)):
1446
buildPrefixFromTree(conditionTree._children[i])
1449
# check for integrity of condition
1451
if len(conditionTree._children) < conditionElements[otype][0]:
1452
tmsg = u_('Condition element "%(element)s" expects at most '
1453
'%(expected)s arguments; found %(found)s') \
1454
% {'element' : otype,
1455
'expected': conditionElements[otype][0],
1456
'found' : len (conditionTree._children)}
1457
raise ConditionError, tmsg
1459
if len(conditionTree._children) > conditionElements[otype][1]:
1460
tmsg = u_('Condition element "%(element)s" expects at most '
1461
'%(expected)s arguments; found %(found)s') \
1462
% {'element' : otype,
1463
'expected': conditionElements[otype][1],
1464
'found' : len (conditionTree._children)}
1465
raise ConditionError, tmsg
1468
# return combination
1472
tmsg = u_('Condition clause "%s" is not supported '
1473
'by the condition to prefix table conversion.') % otype
1474
raise ConditionNotSupported, tmsg
1477
# Combine two conditions with an and clause.
1478
# NOTE: This modifies cond1 (and also returns it)
1480
def __Original__combineConditions (cond1, cond2):
1481
if cond1 == None or cond1 == {}:
1483
elif cond2 == None or cond2 == {}:
1486
if type(cond1) == type({}):
1487
cond1 = buildConditionFromDict(cond1)
1488
if type(cond2) == type({}):
1489
cond2 = buildConditionFromDict(cond2)
1491
if not len(cond1._children):
1492
cond1._children = cond2._children
1494
elif len(cond2._children):
1495
children = cond1._children[:]
1496
cond1._children = []
1498
_and._children = children
1499
if len(cond2._children) > 1:
1500
_and2 = GCand(cond1)
1501
_and2._children = cond2._children[:]
1503
cond1._children.append(cond2._children[0])
1509
# =============================================================================
1510
# Module self test code
1511
# =============================================================================
1513
if __name__ == '__main__':
1515
from mx.DateTime import *
1517
# ---------------------------------------------------------------------------
1518
# self-test-function: unify two arguments
1519
# ---------------------------------------------------------------------------
1521
def _check_unify (values):
1522
args = string.join (["%s (%s)" % (v, type (v).__name__) for v in values])
1523
print "\nUnify:", args
1526
res = string.join (["%s (%s)" % (r, type (r).__name__) \
1527
for r in unify (values)])
1531
print sys.exc_info () [0], sys.exc_info () [1]
1534
_check_unify ([u'17', 5])
1535
_check_unify ([5, '18'])
1536
_check_unify ([now (), '2004-08-15 14:00'])
1537
_check_unify (['13.0', 12])
1538
_check_unify (['15', ''])
1539
_check_unify ([7L, '12'])
1540
_check_unify ([7L, '12.2'])
1541
_check_unify ([1, 'True'])
1544
_check_unify ([None])
1545
_check_unify (['5.43', 12, 18L])
1546
_check_unify ([None, 'foo'])
1547
_check_unify (['foo', None])
1548
_check_unify ([5, None, 2, None, '10'])
1550
print "unification sequence complete"
1554
# ---------------------------------------------------------------------------
1555
# Self-test-function: Construct conditions and evaluate them
1556
# ---------------------------------------------------------------------------
1558
def _check_construction (source, lookup):
1559
if isinstance (source, types.DictType):
1560
cond = buildConditionFromDict (source)
1562
cond = buildTreeFromList (source)
1565
print "Source:", source
1566
print "Prefix:", cond.prefixNotation ()
1567
print "Lookup:", lookup
1568
print "Eval :", cond.evaluate (lookup)
1571
print "Condition tree transformation test"
1573
prefix = ['and', ['eq', ['field', u'foo'], ['const', 'bar']],
1574
['ne', ['field', u'bar'], ['const', 'foobar']]]
1576
lookup = {'foo': 'bar',
1578
_check_construction (prefix, lookup)
1581
prefix = ['or', ['between', ['field', u'foo'],
1582
['const', 'bar'], ['const', 'char']],
1583
['not', ['null', ['field', u'price']]]]
1585
lookup = {'foo': 'capo', 'price': None}
1586
_check_construction (prefix, lookup)
1588
prefix = ['like', ['field', u'foo'], ['const', 'bar%']]
1589
cond = buildTreeFromList (prefix)
1590
lookup = {'foo': 'Barrel'}
1591
_check_construction (prefix, lookup)
1593
lookup = {'foo': 'bar is running'}
1594
_check_construction (prefix, lookup)
1596
source = {'foo': 'bar', 'bar': 'trash'}
1597
lookup = {'foo': 'bar', 'bar': 'trash'}
1598
_check_construction (source, lookup)
1600
prefix = [['eq', ['field', 'foo'], ['const', 'bar']],
1601
['lt', ['field', 'bar'], ['const', 10]]]
1602
lookup = {'foo': 'bar', 'bar': 5.6}
1603
_check_construction (prefix, lookup)
1605
prefix = ['eq', ['upper', ['field', 'nfoo']], ['upper', ['const', 'baR']]]
1606
lookup = {'nfoo': 'bAr'}
1607
_check_construction (prefix, lookup)
1609
prefix = ['eq', ['lower', ['field', 'nfoo']], ['const', 'bar']]
1610
lookup = {'nfoo': 'BaR'}
1611
_check_construction (prefix, lookup)
1613
print "end of construction test sequence"
1616
# ---------------------------------------------------------------------------
1617
# Self-test-function: Combine and evaluate condition trees
1618
# ---------------------------------------------------------------------------
1620
def _check_combineConditions (cond1, cond2):
1623
if not isinstance (cond1, types.DictType):
1624
rc1 = cond1.prefixNotation ()
1625
if not isinstance (cond2, types.DictType):
1626
rc2 = cond2.prefixNotation ()
1628
print "\nCombination of:"
1631
res = combineConditions (cond1, cond2)
1632
print " RES=", res.prefixNotation ()
1635
cond1 = GCondition ()
1636
cond2 = GCondition ()
1638
_check_combineConditions (cond1, cond2)
1640
prefix = ['eq', ['field', 'foo'], ['const', 'bar']]
1641
cond2 = buildTreeFromList (prefix)
1643
_check_combineConditions (cond1, cond2)
1645
cond1 = buildTreeFromList (prefix)
1646
cond2 = GCondition ()
1647
_check_combineConditions (cond1, cond2)
1649
prefix2 = ['and', ['null', ['field', 'nfoo']],
1650
['notnull', ['field', 'nbar']]]
1651
cond2 = buildTreeFromList (prefix2)
1652
_check_combineConditions (cond1, cond2)
1655
cond2 = buildTreeFromList (prefix)
1656
_check_combineConditions (cond1, cond2)
1658
prefix3 = ['null', ['field', 'nfoo']]
1659
cond1 = buildTreeFromList (prefix)
1660
cond2 = buildTreeFromList (prefix3)
1661
_check_combineConditions (cond1, cond2)
1663
prefix4 = [['ne', ['field', 'foo'], ['const', 'bar']],
1664
['lt', ['field', 'price'], ['const', 150.0]]]
1665
cond2 = buildTreeFromList (prefix4)
1666
_check_combineConditions (cond1, cond2)
1668
cond1 = buildTreeFromList (prefix)
1669
_check_combineConditions (cond2, cond1)
1671
prefix = ['or', ['null', ['field', 'nfoo']], ['eq', ['field', 'bar']]]
1672
prefix2 = ['or', ['notnull', ['field', 'nbar']], ['gt', ['field', 'foo']]]
1673
cond1 = buildTreeFromList (prefix)
1674
cond2 = buildTreeFromList (prefix2)
1675
_check_combineConditions (cond1, cond2)
1677
cond1 = buildTreeFromList (['and'])
1678
cond2 = buildTreeFromList (prefix)
1679
_check_combineConditions (cond1, cond2)
1682
prefix = ['exist', 'customer', 'gnue_id', 'invoice.customer',
1683
['or', ['null'], ['field', 'nfoo']]]
1684
cond = buildTreeFromList (prefix)
1685
for i in cond.findChildrenOfType ('GCexist', True, True):
1686
print i.table, i.masterlink, i.detaillink
1688
for c in i._children:
1689
print "C:", c.prefixNotation ()
1691
print "\n\nImpossible condition:", GCimpossible.prefixNotation ()