~ubuntu-branches/ubuntu/hardy/gnue-common/hardy

« back to all changes in this revision

Viewing changes to src/datasources/GConditions.py

  • Committer: Bazaar Package Importer
  • Author(s): Andrew Mitchell
  • Date: 2005-03-09 11:06:31 UTC
  • Revision ID: james.westby@ubuntu.com-20050309110631-8gvvn39q7tjz1kj6
Tags: upstream-0.5.14
ImportĀ upstreamĀ versionĀ 0.5.14

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#
 
2
# This file is part of GNU Enterprise.
 
3
#
 
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.
 
8
#
 
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.
 
13
#
 
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.
 
18
#
 
19
# Copyright 2000-2005 Free Software Foundation
 
20
#
 
21
# $Id: GConditions.py 7057 2005-02-23 16:53:58Z johannes $
 
22
#
 
23
 
 
24
from gnue.common.definitions.GObjects import GObj
 
25
from gnue.common.formatting import GTypecast
 
26
import mx.DateTime
 
27
import types
 
28
import string
 
29
import sys
 
30
import re
 
31
 
 
32
class ConditionError (gException):
 
33
  pass
 
34
 
 
35
class ConditionNotSupported (ConditionError):
 
36
  pass
 
37
 
 
38
class MalformedConditionTreeError (ConditionError):
 
39
  pass
 
40
 
 
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],
 
46
             'wanted' : wanted,
 
47
             'real'   : len (element._children)}
 
48
    MalformedConditionTreeError.__init__ (self, msg)
 
49
 
 
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)
 
55
 
 
56
 
 
57
class UnificationError (gException):
 
58
  pass
 
59
 
 
60
class ConversionRuleError (UnificationError):
 
61
  def __init__ (self, value1, value2):
 
62
    msg = u_("No unification rule for combination '%(type1)s' and "
 
63
             "'%(type2)s'") \
 
64
          % {'type1': type (value1).__name__,
 
65
             'type2': type (value2).__name__}
 
66
    UnificationError.__init__ (self, msg)
 
67
 
 
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'") \
 
72
          % {'value1': value1,
 
73
             'type1' : type (value1).__name__,
 
74
             'type2' : type (value2).__name__}
 
75
    UnificationError.__init__ (self, msg)
 
76
 
 
77
 
 
78
 
 
79
# =============================================================================
 
80
# Base condition class; this is class is acts as root node of condition trees
 
81
# =============================================================================
 
82
 
 
83
class GCondition (GObj):
 
84
  """
 
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.
 
88
  """
 
89
  def __init__(self, parent = None, type = "GCCondition"):
 
90
    GObj.__init__ (self, parent, type = type)
 
91
    self._maxChildren = None
 
92
 
 
93
 
 
94
  # ---------------------------------------------------------------------------
 
95
  # Make sure an element of the tree has the requested number of children
 
96
  # ---------------------------------------------------------------------------
 
97
 
 
98
  def _needChildren (self, number):
 
99
    """
 
100
    This function verifies if a condition element has a given number of
 
101
    children. If not an ArgumentCountError will be raised.
 
102
    """
 
103
    if number is not None and len (self._children) != number:
 
104
      raise ArgumentCountError, (self, number)
 
105
 
 
106
 
 
107
  # ---------------------------------------------------------------------------
 
108
  # Evaluate a condition tree using the given lookup dictionary
 
109
  # ---------------------------------------------------------------------------
 
110
 
 
111
  def evaluate (self, lookup):
 
112
    """
 
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.
 
116
    """
 
117
    self.validate ()
 
118
 
 
119
    for child in self._children:
 
120
      if not child.evaluate (lookup):
 
121
        return False
 
122
 
 
123
    return True
 
124
 
 
125
 
 
126
  # ---------------------------------------------------------------------------
 
127
  # Validate an element of a condition tree
 
128
  # ---------------------------------------------------------------------------
 
129
 
 
130
  def validate (self):
 
131
    """
 
132
    This function calls validate () on all it's children. Descendants might
 
133
    override this function to do integrity checks and things like that.
 
134
    """
 
135
    self._needChildren (self._maxChildren)
 
136
 
 
137
    for child in self._children:
 
138
      child.validate ()
 
139
 
 
140
 
 
141
  # ---------------------------------------------------------------------------
 
142
  # Convert an element into prefix notation
 
143
  # ---------------------------------------------------------------------------
 
144
 
 
145
  def prefixNotation (self):
 
146
    """
 
147
    This function returns the prefix notation of an element and all it's
 
148
    children.
 
149
    """
 
150
    result = []
 
151
    append = result.extend
 
152
 
 
153
    if isinstance (self, GConditionElement):
 
154
      result.append (self._type [2:])
 
155
      append = result.append
 
156
    
 
157
    for child in self._children:
 
158
      append (child.prefixNotation ())
 
159
 
 
160
    return result
 
161
 
 
162
 
 
163
  # ---------------------------------------------------------------------------
 
164
  # Build an element and all it's children from a prefix notation list
 
165
  # ---------------------------------------------------------------------------
 
166
 
 
167
  def buildFromList (self, prefixList):
 
168
    """
 
169
    This function creates a (partial) condition tree from a prefix notation
 
170
    list.
 
171
    """
 
172
    checktype (prefixList, types.ListType)
 
173
 
 
174
    if len (prefixList):
 
175
      item = prefixList [0]
 
176
 
 
177
      # be nice if there's a condition part missing
 
178
      offset = 1
 
179
      if isinstance (item, types.ListType):
 
180
        self.buildFromList (item)
 
181
        element = self
 
182
      else:
 
183
        # automatically map 'field' to 'Field' and 'const' to 'Const'
 
184
        if item in ['field', 'const']:
 
185
          item = item.title ()
 
186
 
 
187
        element = getattr (sys.modules [__name__], "GC%s" % item) (self)
 
188
 
 
189
        if item == 'exist':
 
190
          (table, masterlink, detaillink) = prefixList [1:4]
 
191
          element.table      = table
 
192
          element.masterlink = masterlink
 
193
          element.detaillink = detaillink
 
194
          offset = 4
 
195
 
 
196
      for subitem in prefixList [offset:]:
 
197
        element.buildFromList (subitem)
 
198
 
 
199
 
 
200
  # ---------------------------------------------------------------------------
 
201
  # Break up all top-down references
 
202
  # ---------------------------------------------------------------------------
 
203
 
 
204
  def breakReferences (self):
 
205
    """
 
206
    This function resolves the reference to the parent instance avoiding
 
207
    reference cycles.
 
208
    """
 
209
 
 
210
    self._parent = None
 
211
    for item in self._children:
 
212
      item.breakReferences ()
 
213
 
 
214
 
 
215
# =============================================================================
 
216
# Top level classes 
 
217
# =============================================================================
 
218
 
 
219
class GConditions(GCondition):
 
220
  def __init__(self, parent=None, type="GCConditions"):
 
221
    GCondition.__init__(self, parent, type = type)
 
222
 
 
223
class GConditionElement (GCondition) :
 
224
  def __init__(self, parent=None, type="GConditionElement"):
 
225
    GCondition.__init__ (self, parent, type = type)
 
226
 
 
227
 
 
228
# -----------------------------------------------------------------------------
 
229
# A Field element in the condition tree
 
230
# -----------------------------------------------------------------------------
 
231
 
 
232
class GCField (GConditionElement):
 
233
  def __init__(self, parent, name = None, datatype = "char"):
 
234
    GConditionElement.__init__ (self, parent, 'GCCField')
 
235
    self.type = datatype
 
236
    self.name = name
 
237
 
 
238
  # ---------------------------------------------------------------------------
 
239
  # Evaluate a field element
 
240
  # ---------------------------------------------------------------------------
 
241
 
 
242
  def evaluate (self, lookup):
 
243
    """
 
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
 
246
    raised.
 
247
    """
 
248
    if not lookup.has_key (self.name):
 
249
      raise MissingFieldError, (self.name)
 
250
 
 
251
    return lookup [self.name]
 
252
 
 
253
 
 
254
  # ---------------------------------------------------------------------------
 
255
  # A field in prefix notation is a tuple of 'field' and fieldname
 
256
  # ---------------------------------------------------------------------------
 
257
 
 
258
  def prefixNotation (self):
 
259
    """
 
260
    The prefix notation of a field element is a tuple of the identifier 'field'
 
261
    (acting as operator) and the field's name.
 
262
    """
 
263
    return ['Field', self.name]
 
264
 
 
265
 
 
266
  # ---------------------------------------------------------------------------
 
267
  # to complete a field element from a prefix notation set the fieldname
 
268
  # ---------------------------------------------------------------------------
 
269
 
 
270
  def buildFromList (self, prefixList):
 
271
    """
 
272
    The single argument to a field 'operator' could be it's name, so this
 
273
    method set's the fieldname.
 
274
    """
 
275
    checktype (prefixList, [types.StringType, types.UnicodeType])
 
276
    self.name = prefixList
 
277
 
 
278
 
 
279
# -----------------------------------------------------------------------------
 
280
# A constant definition in a condition tree
 
281
# -----------------------------------------------------------------------------
 
282
 
 
283
class GCConst (GConditionElement):
 
284
  def __init__ (self, parent, value = None, datatype = "char"):
 
285
    GConditionElement.__init__ (self, parent, 'GCCConst')
 
286
    self.type  = datatype
 
287
    self.value = value
 
288
 
 
289
 
 
290
  # ---------------------------------------------------------------------------
 
291
  # Evaluate a constant
 
292
  # ---------------------------------------------------------------------------
 
293
 
 
294
  def evaluate (self, lookup):
 
295
    """
 
296
    This function returns the constants value
 
297
    """
 
298
    return self.value
 
299
 
 
300
 
 
301
  # ---------------------------------------------------------------------------
 
302
  # The prefix notation of a constant is a tuple of identifier and value
 
303
  # ---------------------------------------------------------------------------
 
304
 
 
305
  def prefixNotation (self):
 
306
    """
 
307
    The prefix notation of a constant is a tuple of the identifier 'Const' and
 
308
    the constant's value.
 
309
    """
 
310
    return ['Const', self.value]
 
311
 
 
312
 
 
313
  # ---------------------------------------------------------------------------
 
314
  # Recreate a constant from a prefix notation
 
315
  # ---------------------------------------------------------------------------
 
316
 
 
317
  def buildFromList (self, prefixList):
 
318
    """
 
319
    The single argument of a constant 'operator' could be it's value, so this
 
320
    function set the constant's value.
 
321
    """
 
322
    self.value = prefixList
 
323
 
 
324
 
 
325
# -----------------------------------------------------------------------------
 
326
# Base class for parameter elements in a condition tree
 
327
# -----------------------------------------------------------------------------
 
328
 
 
329
class GCParam (GConditionElement):
 
330
  def __init__ (self, parent, name = None, datatype = "char"):
 
331
    GConditionElement.__init__ (self, parent, 'GCCParam')
 
332
    self.type = datatype
 
333
    self.name = name
 
334
 
 
335
  # ---------------------------------------------------------------------------
 
336
  # Return the value of a parameter
 
337
  # ---------------------------------------------------------------------------
 
338
 
 
339
  def getValue(self):
 
340
    """
 
341
    Descendants override this function to return the value of the parameter.
 
342
    """
 
343
    return ""
 
344
 
 
345
 
 
346
  # ---------------------------------------------------------------------------
 
347
  # Evaluate the parameter object
 
348
  # ---------------------------------------------------------------------------
 
349
 
 
350
  def evaluate (self, lookup):
 
351
    """
 
352
    A parameter element evaluates to it's value.
 
353
    """
 
354
    return self.getValue ()
 
355
 
 
356
 
 
357
  # ---------------------------------------------------------------------------
 
358
  # Return a parameter object in prefix notation
 
359
  # ---------------------------------------------------------------------------
 
360
 
 
361
  def prefixNotation (self):
 
362
    """
 
363
    The prefix notation of a parameter object is a 'constant' with the
 
364
    parameters' value
 
365
    """
 
366
    return ['Const', self.getValue ()]
 
367
 
 
368
 
 
369
# =============================================================================
 
370
# Base classes for unary and binary operations
 
371
# =============================================================================
 
372
 
 
373
class GUnaryConditionElement (GConditionElement):
 
374
  def __init__ (self, parent = None, elementType = ''):
 
375
    self._maxChildren = 1
 
376
    GConditionElement.__init__ (self, parent, elementType)
 
377
 
 
378
class GBinaryConditionElement (GConditionElement):
 
379
  def __init__ (self, parent = None, elementType = ''):
 
380
    GConditionElement.__init__ (self, parent, elementType)
 
381
    self._maxChildren = 2
 
382
    self.values       = []
 
383
 
 
384
  # ---------------------------------------------------------------------------
 
385
  # Evaluating a binary element means evaluation of both children
 
386
  # ---------------------------------------------------------------------------
 
387
 
 
388
  def evaluate (self, lookup):
 
389
    """
 
390
    This function evaluates both children of a binary element storing their
 
391
    values in the property 'values'. Descendants can use these values for
 
392
    further evaluations.
 
393
    """
 
394
    self._needChildren (self._maxChildren)
 
395
    self.values = unify ([child.evaluate (lookup) for child in self._children])
 
396
 
 
397
 
 
398
# =============================================================================
 
399
# Logical operators
 
400
# =============================================================================
 
401
 
 
402
# -----------------------------------------------------------------------------
 
403
# n-ary operation: AND
 
404
# -----------------------------------------------------------------------------
 
405
 
 
406
class GCand (GConditionElement):
 
407
  def __init__ (self, parent = None):
 
408
    GConditionElement.__init__ (self, parent, 'GCand')
 
409
 
 
410
 
 
411
# -----------------------------------------------------------------------------
 
412
# n-ary operation: OR
 
413
# -----------------------------------------------------------------------------
 
414
 
 
415
class GCor (GConditionElement):
 
416
  def __init__ (self, parent = None):
 
417
    GConditionElement.__init__ (self, parent, 'GCor')
 
418
 
 
419
  # ---------------------------------------------------------------------------
 
420
  # Evaluate an OR tree
 
421
  # ---------------------------------------------------------------------------
 
422
 
 
423
  def evaluate (self, lookup):
 
424
    """
 
425
    This function concatenates all children of this element by a logical OR.
 
426
    The iteration stops on the first 'true' result.
 
427
    """
 
428
    for child in self._children:
 
429
      if child.evaluate (lookup):
 
430
        return True
 
431
 
 
432
    return False
 
433
 
 
434
# -----------------------------------------------------------------------------
 
435
# unary operation: NOT
 
436
# -----------------------------------------------------------------------------
 
437
 
 
438
class GCnot (GUnaryConditionElement):
 
439
  def __init__ (self, parent = None):
 
440
    GUnaryConditionElement.__init__ (self, parent, 'GCnot')
 
441
 
 
442
  # ---------------------------------------------------------------------------
 
443
  # logically Invert the childs result
 
444
  # ---------------------------------------------------------------------------
 
445
 
 
446
  def evaluate (self, lookup):
 
447
    """
 
448
    This function logically inverts the child's evaluation
 
449
    """
 
450
    self._needChildren (self._maxChildren)
 
451
    return not self._children [0].evaluate (lookup)
 
452
 
 
453
 
 
454
 
 
455
# =============================================================================
 
456
# Numeric operations
 
457
# =============================================================================
 
458
 
 
459
# ---------------------------------------------------------------------------
 
460
# n-ary operation: Addition
 
461
# ---------------------------------------------------------------------------
 
462
 
 
463
class GCadd (GConditionElement):
 
464
  def __init__ (self, parent = None):
 
465
    GConditionElement.__init__ (self, parent, 'GCadd')
 
466
 
 
467
  # ---------------------------------------------------------------------------
 
468
  # Evaluate the addition element
 
469
  # ---------------------------------------------------------------------------
 
470
  def evaluate (self, lookup):
 
471
    """
 
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.
 
474
    """
 
475
    result = 0
 
476
    for child in self._children:
 
477
      result += unify ([child.evaluation (lookup), 0]) [0]
 
478
    return result
 
479
 
 
480
 
 
481
# -----------------------------------------------------------------------------
 
482
# n-ary operation: Subtraction
 
483
# -----------------------------------------------------------------------------
 
484
 
 
485
class GCsub (GConditionElement):
 
486
  def __init__ (self, parent = None):
 
487
    GConditionElement.__init__ (self, parent, 'GCsub')
 
488
 
 
489
  # ---------------------------------------------------------------------------
 
490
  # Evaluate the subtraction element
 
491
  # ---------------------------------------------------------------------------
 
492
 
 
493
  def evaluate (self, lookup):
 
494
    result = None
 
495
 
 
496
    for child in self._children:
 
497
      value = unify ([child.evaluation (lookup), 0]) [0]
 
498
      if result is None:
 
499
        result = value
 
500
      else:
 
501
        result -= value
 
502
 
 
503
    return result
 
504
 
 
505
 
 
506
# -----------------------------------------------------------------------------
 
507
# n-ary operation: Multiplication
 
508
# -----------------------------------------------------------------------------
 
509
 
 
510
class GCmul (GConditionElement):
 
511
  def __init__ (self, parent = None):
 
512
    GConditionElement.__init__ (self, parent, 'GCmul')
 
513
 
 
514
  # ---------------------------------------------------------------------------
 
515
  # Evaluate the multiplication
 
516
  # ---------------------------------------------------------------------------
 
517
 
 
518
  def evaluate (self, lookup):
 
519
    result = None
 
520
 
 
521
    for child in self._children:
 
522
      value = unify ([child.evaluate (lookup), 0]) [0]
 
523
      if result is None:
 
524
        result = value
 
525
      else:
 
526
        result *= value
 
527
 
 
528
    return result
 
529
 
 
530
 
 
531
# -----------------------------------------------------------------------------
 
532
# n-ary operation: Division
 
533
# -----------------------------------------------------------------------------
 
534
 
 
535
class GCdiv (GConditionElement):
 
536
  def __init__ (self, parent = None):
 
537
    GConditionElement.__init__ (self, parent, 'GCdiv')
 
538
 
 
539
  # ---------------------------------------------------------------------------
 
540
  # Evaluate the division element
 
541
  # ---------------------------------------------------------------------------
 
542
 
 
543
  def evaluate (self, lookup):
 
544
    result = None
 
545
 
 
546
    for child in self._children:
 
547
      value = unify ([child.evaluate (lookup), 0]) [0]
 
548
      if result is None:
 
549
        result = value
 
550
      else:
 
551
        result /= value
 
552
 
 
553
    return result
 
554
 
 
555
# -----------------------------------------------------------------------------
 
556
# unary operation: numeric negation
 
557
# -----------------------------------------------------------------------------
 
558
 
 
559
class GCnegate (GUnaryConditionElement):
 
560
  def __init__ (self, parent = None):
 
561
    GUnaryConditionElement.__init__ (self, parent, 'GCnegate')
 
562
 
 
563
  # ---------------------------------------------------------------------------
 
564
  # Evaluation of the numeric negation
 
565
  # ---------------------------------------------------------------------------
 
566
 
 
567
  def evaluate (self, lookup):
 
568
    """
 
569
    This function does a numeric negation on the child's evaluation result.
 
570
    """
 
571
    self._needChildren (self._maxChildren)
 
572
    return -unify ([self._children [0].evaluate (lookup), 0]) [0]
 
573
    
 
574
 
 
575
# =============================================================================
 
576
# Relational operations
 
577
# =============================================================================
 
578
 
 
579
# -----------------------------------------------------------------------------
 
580
# Equality
 
581
# -----------------------------------------------------------------------------
 
582
 
 
583
class GCeq (GBinaryConditionElement):
 
584
  def __init__ (self, parent = None):
 
585
    GBinaryConditionElement.__init__ (self, parent, 'GCeq')
 
586
 
 
587
  # ---------------------------------------------------------------------------
 
588
  # evaluate EQ relation
 
589
  # ---------------------------------------------------------------------------
 
590
 
 
591
  def evaluate (self, lookup):
 
592
    GBinaryConditionElement.evaluate (self, lookup)
 
593
    return self.values [0] == self.values [1]
 
594
 
 
595
 
 
596
# -----------------------------------------------------------------------------
 
597
# Inequality
 
598
# -----------------------------------------------------------------------------
 
599
 
 
600
class GCne (GBinaryConditionElement):
 
601
  def __init__ (self, parent = None):
 
602
    GBinaryConditionElement.__init__ (self, parent, 'GCne')
 
603
 
 
604
  # ---------------------------------------------------------------------------
 
605
  # evaluate NE relation
 
606
  # ---------------------------------------------------------------------------
 
607
 
 
608
  def evaluate (self, lookup):
 
609
    GBinaryConditionElement.evaluate (self, lookup)
 
610
    return self.values [0] != self.values [1]
 
611
 
 
612
 
 
613
# -----------------------------------------------------------------------------
 
614
# Greater Than
 
615
# -----------------------------------------------------------------------------
 
616
 
 
617
class GCgt (GBinaryConditionElement):
 
618
  def __init__ (self, parent = None):
 
619
    GBinaryConditionElement.__init__ (self, parent, 'GCgt')
 
620
 
 
621
  # ---------------------------------------------------------------------------
 
622
  # evaluate GT relation
 
623
  # ---------------------------------------------------------------------------
 
624
 
 
625
  def evaluate (self, lookup):
 
626
    GBinaryConditionElement.evaluate (self, lookup)
 
627
    return self.values [0] > self.values [1]
 
628
 
 
629
 
 
630
# -----------------------------------------------------------------------------
 
631
# Greater or Equal
 
632
# -----------------------------------------------------------------------------
 
633
 
 
634
class GCge (GBinaryConditionElement):
 
635
  def __init__ (self, parent = None):
 
636
    GBinaryConditionElement.__init__ (self, parent, 'GCge')
 
637
 
 
638
  # ---------------------------------------------------------------------------
 
639
  # evaluate GE relation
 
640
  # ---------------------------------------------------------------------------
 
641
 
 
642
  def evaluate (self, lookup):
 
643
    GBinaryConditionElement.evaluate (self, lookup)
 
644
    return self.values [0] >= self.values [1]
 
645
 
 
646
 
 
647
 
 
648
# -----------------------------------------------------------------------------
 
649
# Less Than
 
650
# -----------------------------------------------------------------------------
 
651
 
 
652
class GClt (GBinaryConditionElement):
 
653
  def __init__ (self, parent = None):
 
654
    GBinaryConditionElement.__init__ (self, parent, 'GClt')
 
655
 
 
656
  # ---------------------------------------------------------------------------
 
657
  # evaluate LT relation
 
658
  # ---------------------------------------------------------------------------
 
659
 
 
660
  def evaluate (self, lookup):
 
661
    GBinaryConditionElement.evaluate (self, lookup)
 
662
    return self.values [0] < self.values [1]
 
663
 
 
664
 
 
665
# -----------------------------------------------------------------------------
 
666
# Less or Equal
 
667
# -----------------------------------------------------------------------------
 
668
 
 
669
class GCle (GBinaryConditionElement):
 
670
  def __init__ (self, parent = None):
 
671
    GBinaryConditionElement.__init__ (self, parent, 'GCle')
 
672
 
 
673
  # ---------------------------------------------------------------------------
 
674
  # evaluate LE relation
 
675
  # ---------------------------------------------------------------------------
 
676
 
 
677
  def evaluate (self, lookup):
 
678
    GBinaryConditionElement.evaluate (self, lookup)
 
679
    return self.values [0] <= self.values [1]
 
680
 
 
681
 
 
682
# -----------------------------------------------------------------------------
 
683
# Like
 
684
# -----------------------------------------------------------------------------
 
685
 
 
686
class GClike (GBinaryConditionElement):
 
687
  def __init__ (self, parent = None):
 
688
    GBinaryConditionElement.__init__ (self, parent, 'GClike')
 
689
 
 
690
  # ---------------------------------------------------------------------------
 
691
  # Evaluate a like condition
 
692
  # ---------------------------------------------------------------------------
 
693
 
 
694
  def evaluate (self, lookup):
 
695
    GBinaryConditionElement.evaluate (self, lookup)
 
696
    # None cannot be like something else. You should use 'NULL' or 'NOT NULL'
 
697
    # instead
 
698
    if self.values [0] is None:
 
699
      return False
 
700
 
 
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
 
705
 
 
706
 
 
707
# -----------------------------------------------------------------------------
 
708
# Not Like
 
709
# -----------------------------------------------------------------------------
 
710
 
 
711
class GCnotlike (GBinaryConditionElement):
 
712
  def __init__ (self, parent = None):
 
713
    GBinaryConditionElement.__init__ (self, parent, 'GCnotlike')
 
714
 
 
715
  # ---------------------------------------------------------------------------
 
716
  # Evaluate an inverted like condition
 
717
  # ---------------------------------------------------------------------------
 
718
 
 
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
 
725
 
 
726
 
 
727
# -----------------------------------------------------------------------------
 
728
# Between
 
729
# -----------------------------------------------------------------------------
 
730
 
 
731
class GCbetween (GConditionElement):
 
732
  def __init__ (self, parent = None):
 
733
    self._maxChildren = 3
 
734
    GConditionElement.__init__ (self, parent, 'GCbetween')
 
735
 
 
736
  # ---------------------------------------------------------------------------
 
737
  # evaluate beetween relation
 
738
  # ---------------------------------------------------------------------------
 
739
 
 
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]
 
744
 
 
745
 
 
746
# -----------------------------------------------------------------------------
 
747
# Not Between
 
748
# -----------------------------------------------------------------------------
 
749
 
 
750
class GCnotbetween (GConditionElement):
 
751
  def __init__ (self, parent = None):
 
752
    self._maxChildren = 3
 
753
    GConditionElement.__init__ (self, parent, 'GCnotbetween')
 
754
 
 
755
  # ---------------------------------------------------------------------------
 
756
  # evaluate an inverted beetween relation
 
757
  # ---------------------------------------------------------------------------
 
758
 
 
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])
 
763
 
 
764
 
 
765
# -----------------------------------------------------------------------------
 
766
# is NULL
 
767
# -----------------------------------------------------------------------------
 
768
 
 
769
class GCnull (GUnaryConditionElement):
 
770
  def __init__ (self, parent = None):
 
771
    GUnaryConditionElement.__init__ (self, parent, 'GCnull')
 
772
 
 
773
  # ---------------------------------------------------------------------------
 
774
  # evaluate if a child is NULL
 
775
  # ---------------------------------------------------------------------------
 
776
 
 
777
  def evaluate (self, lookup):
 
778
    self._needChildren (self._maxChildren)
 
779
    return self._children [0].evaluate (lookup) is None
 
780
 
 
781
 
 
782
# -----------------------------------------------------------------------------
 
783
# is Not NULL
 
784
# -----------------------------------------------------------------------------
 
785
 
 
786
class GCnotnull (GUnaryConditionElement):
 
787
  def __init__ (self, parent = None):
 
788
    GUnaryConditionElement.__init__ (self, parent, 'GCnotnull')
 
789
 
 
790
  # ---------------------------------------------------------------------------
 
791
  # evaluate if a child is not NULL
 
792
  # ---------------------------------------------------------------------------
 
793
 
 
794
  def evaluate (self, lookup):
 
795
    self._needChildren (self._maxChildren)
 
796
    return self._children [0].evaluate (lookup) is not None
 
797
 
 
798
# -----------------------------------------------------------------------------
 
799
# upper  
 
800
# -----------------------------------------------------------------------------
 
801
 
 
802
class GCupper (GUnaryConditionElement):
 
803
  def __init__ (self, parent = None):
 
804
    GUnaryConditionElement.__init__ (self, parent, 'GCupper')
 
805
 
 
806
  # ---------------------------------------------------------------------------
 
807
  # evaluate
 
808
  # ---------------------------------------------------------------------------
 
809
 
 
810
  def evaluate (self, lookup):
 
811
    self._needChildren (self._maxChildren)
 
812
    return string.upper (self._children [0].evaluate (lookup))
 
813
 
 
814
# -----------------------------------------------------------------------------
 
815
# lower  
 
816
# -----------------------------------------------------------------------------
 
817
 
 
818
class GClower (GUnaryConditionElement):
 
819
  def __init__ (self, parent = None):
 
820
    GUnaryConditionElement.__init__ (self, parent, 'GClower')
 
821
 
 
822
  # ---------------------------------------------------------------------------
 
823
  # evaluate
 
824
  # ---------------------------------------------------------------------------
 
825
 
 
826
  def evaluate (self, lookup):
 
827
    self._needChildren (self._maxChildren)
 
828
    return string.lower (self._children [0].evaluate (lookup))
 
829
 
 
830
 
 
831
# -----------------------------------------------------------------------------
 
832
# exist
 
833
# -----------------------------------------------------------------------------
 
834
 
 
835
class GCexist (GConditionElement):
 
836
  def __init__ (self, parent = None):
 
837
    GConditionElement.__init__ (self, parent, 'GCexist')
 
838
    self.callback = None
 
839
 
 
840
  # ---------------------------------------------------------------------------
 
841
  # Evaluate an exist-condition
 
842
  # ---------------------------------------------------------------------------
 
843
 
 
844
  def evaluate (self, lookup):
 
845
 
 
846
    if self.callback is None:
 
847
      raise NotImplementedError
 
848
 
 
849
    return self.callback (self, lookup)
 
850
 
 
851
 
 
852
  # ---------------------------------------------------------------------------
 
853
  # Convert an element into prefix notation
 
854
  # ---------------------------------------------------------------------------
 
855
 
 
856
  def prefixNotation (self):
 
857
    """
 
858
    This function returns the prefix notation of an exist element and all it's
 
859
    children.
 
860
    """
 
861
    result = ['exist', self.table, self.masterlink, self.detaillink]
 
862
    
 
863
    for child in self._children:
 
864
      result.append (child.prefixNotation ())
 
865
 
 
866
    return result
 
867
 
 
868
 
 
869
 
 
870
# =============================================================================
 
871
# Return a dictionary of all XML elements available
 
872
# =============================================================================
 
873
 
 
874
def getXMLelements (updates = {}):
 
875
  xmlElements = {
 
876
      'conditions':       {
 
877
         'BaseClass': GCondition,
 
878
         'ParentTags':  ('conditions','and','or','not','negate'),
 
879
         'Deprecated': 'Use the <condition> tag instead.',
 
880
         },
 
881
      'condition':       {
 
882
         'BaseClass': GCondition,
 
883
         'ParentTags':  ('conditions','and','or','not','negate','exist') },
 
884
      'cfield':       {
 
885
         'BaseClass': GCField,
 
886
         'Description': 'Defines a database table\'s field in a condition.',
 
887
         'Attributes': {
 
888
            'name':     {
 
889
               'Required': True,
 
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') },
 
894
      'cparam':       {
 
895
         'BaseClass': GCParam,
 
896
         'Description': 'Defines a parameter value in a condition.',
 
897
         'Attributes': {
 
898
            'name':        {
 
899
               'Required': True,
 
900
               'Unique':   True,
 
901
               'Typecast': GTypecast.name } },
 
902
         'ParentTags':  ('eq','ne','lt','le','gt','ge','add','sub','mul',
 
903
                         'div','like','notlike','between','notbetween') },
 
904
      'cconst':       {
 
905
         'BaseClass': GCConst,
 
906
         'Description': 'Defines a constant value in a condition.',
 
907
         'Attributes': {
 
908
            'value':     {
 
909
               'Required': True,
 
910
               'Typecast': GTypecast.text },
 
911
            'type':     {
 
912
               'Typecast': GTypecast.text } },
 
913
         'ParentTags':  ('eq','ne','lt','le','gt','ge','add','sub','mul',
 
914
                         'div','like','notlike','between','notbetween') },
 
915
      'add':       {
 
916
         'BaseClass': GCadd,
 
917
         'Description': 'Implements addition.',
 
918
         'ParentTags':  ('eq','ne','lt','le','gt','ge','add','sub','mul',
 
919
                         'div','like','notlike','between','notbetween') },
 
920
      'sub':       {
 
921
         'BaseClass': GCsub,
 
922
         'Description': 'Implements subtraction.',
 
923
         'ParentTags':  ('eq','ne','lt','le','gt','ge','add','sub','mul',
 
924
                         'div','like','notlike','between','notbetween') },
 
925
      'mul':       {
 
926
         'BaseClass': GCmul,
 
927
         'Description': 'Implements multiplication.',
 
928
         'ParentTags':  ('eq','ne','lt','le','gt','ge','add','sub','mul',
 
929
                         'div','like','notlike','between','notbetween') },
 
930
      'div':       {
 
931
         'BaseClass': GCdiv,
 
932
         'Description': 'Implements division.',
 
933
         'ParentTags':  ('eq','ne','lt','le','gt','ge','add','sub','mul',
 
934
                         'div','like','notlike','between','notbetween') },
 
935
      'and':       {
 
936
         'BaseClass': GCand,
 
937
         'Description': 'Implements logical AND relation.',
 
938
         'ParentTags':  ('condition','and','or','not','negate') },
 
939
      'or':       {
 
940
         'BaseClass': GCor,
 
941
         'Description': 'Implements logical OR relation.',
 
942
         'ParentTags':  ('condition','and','or','not','negate') },
 
943
      'not':       {
 
944
         'BaseClass': GCnot,
 
945
         'Description': 'Implements logical NOT relation.',
 
946
         'ParentTags':  ('condition','and','or','not','negate') },
 
947
      'negate':       {
 
948
         'BaseClass': GCnegate,
 
949
         'Description': 'Implements numerical negation.',
 
950
         'ParentTags':  ('condition','and','or','not','negate') },
 
951
      'eq':       {
 
952
         'BaseClass': GCeq,
 
953
         'Description': 'Implements a {field} = {value} condition.',
 
954
         'ParentTags':  ('condition','and','or','not','negate') },
 
955
      'ne':       {
 
956
         'BaseClass': GCne,
 
957
         'Description': 'Implements a {field} <> {value} condition.',
 
958
         'ParentTags':  ('condition','and','or','not','negate') },
 
959
      'gt':       {
 
960
         'BaseClass': GCgt,
 
961
         'Description': 'Implements a {field} > {value} condition.',
 
962
         'ParentTags':  ('condition','and','or','not','negate') },
 
963
      'ge':       {
 
964
         'BaseClass': GCge,
 
965
         'Description': 'Implements a {field} >= {value} condition.',
 
966
         'ParentTags':  ('condition','and','or','not','negate') },
 
967
      'lt':       {
 
968
         'BaseClass': GClt,
 
969
         'Description': 'Implements a {field} < {value} condition.',
 
970
         'ParentTags':  ('condition','and','or','not','negate') },
 
971
      'le':       {
 
972
         'BaseClass': GCle,
 
973
         'Description': 'Implements a {field} <= {value} condition.',
 
974
         'ParentTags':  ('condition','and','or','not','negate') },
 
975
      'like':       {
 
976
         'BaseClass': GClike,
 
977
         'Description': 'Implements a {field} LIKE {value} condition.',
 
978
         'ParentTags':  ('condition','and','or','not','negate') },
 
979
      'notlike':       {
 
980
         'BaseClass': GCnotlike,
 
981
         'Description': 'Implements a {field} NOT LIKE {value} condition.',
 
982
         'ParentTags':  ('condition','and','or','not','negate') },
 
983
      'between':       {
 
984
         'BaseClass': GCbetween,
 
985
         'Description': 'Implements a {field} BETWEEN {value1} {value2} '
 
986
                        'condition.',
 
987
         'ParentTags':  ('condition','and','or','not','negate') },
 
988
      'notbetween':       {
 
989
         'BaseClass': GCnotbetween,
 
990
         'Description': 'Implements a {field} NOT BETWEEN {value1} {value2} '
 
991
                        'condition.',
 
992
         'ParentTags':  ('condition','and','or','not','negate') },
 
993
      'null':      {
 
994
         'BaseClass': GCnull,
 
995
         'Description': 'Implements a {field} IS NULL condition.',
 
996
         'ParentTags': ('condition','and','or','not') },
 
997
      'notnull':      {
 
998
         'BaseClass': GCnotnull,
 
999
         'Description': 'Implements a {field} IS NOT NULL condition.',
 
1000
         'ParentTags': ('condition','and','or','not') },
 
1001
      'upper':       {
 
1002
         'BaseClass': GCupper,
 
1003
         'Description': 'Implements upper({value}).',
 
1004
         'ParentTags':  ('eq','ne','lt','le','gt','ge',
 
1005
                         'like','notlike','between','notbetween') },
 
1006
      'lower':       {
 
1007
         'BaseClass': GClower,
 
1008
         'Description': 'Implements lower({value}).',
 
1009
         'ParentTags':  ('eq','ne','lt','le','gt','ge',
 
1010
                         'like','notlike','between','notbetween') },
 
1011
      'exist': {
 
1012
         'BaseClass': GCexist,
 
1013
         'Description': 'Implements an exist condition.',
 
1014
         'Attributes': {
 
1015
            'table': {
 
1016
               'Required': True,
 
1017
               'Description': 'TODO',
 
1018
               'Typecast': GTypecast.name },
 
1019
            'masterlink': {
 
1020
               'Required': True,
 
1021
               'Description': 'TODO',
 
1022
               'Typecast': GTypecast.text},
 
1023
            'detaillink': {
 
1024
               'Required': True,
 
1025
               'Description': 'TODO',
 
1026
               'Typecast': GTypecast.text}},
 
1027
         'ParentTags':  ('eq','ne','lt','le','gt','ge',
 
1028
                         'like','notlike','between','notbetween') },
 
1029
      }
 
1030
 
 
1031
  for alteration in updates.keys():
 
1032
    xmlElements[alteration].update(updates[alteration])
 
1033
 
 
1034
  return xmlElements
 
1035
 
 
1036
###############################################################################
 
1037
###############################################################################
 
1038
####                        Convenience Methods                              ##
 
1039
###############################################################################
 
1040
###############################################################################
 
1041
 
 
1042
# =============================================================================
 
1043
# Create a condition tree from an element sequence in prefix notation
 
1044
# =============================================================================
 
1045
 
 
1046
def buildTreeFromList (prefixList):
 
1047
  """
 
1048
  This function creates a new condition tree from the given element sequence,
 
1049
  which must be in prefix notation.
 
1050
  """
 
1051
  checktype (prefixList, types.ListType)
 
1052
 
 
1053
  result = GCondition ()
 
1054
  result.buildFromList (prefixList)
 
1055
  return result
 
1056
 
 
1057
 
 
1058
# =============================================================================
 
1059
# Create a condition tree from a dictionary
 
1060
# =============================================================================
 
1061
 
 
1062
def buildConditionFromDict (dictionary, comparison = GCeq, logic = GCand):
 
1063
  """
 
1064
  This function creates a new condition tree using @comparison as operation
 
1065
  between keys and values and @logic as concatenation for all keys.
 
1066
  """
 
1067
  result = GCondition ()
 
1068
  lastParent = result
 
1069
 
 
1070
  if len (dictionary.keys ()):
 
1071
    lastParent = logic (lastParent)
 
1072
 
 
1073
  for key, value in dictionary.items ():
 
1074
    operation = comparison (lastParent)
 
1075
    GCField (operation, key)
 
1076
 
 
1077
    if type (value) in [types.IntType, types.FloatType, types.LongType]:
 
1078
      consttype = 'number'
 
1079
    else:
 
1080
      consttype = 'char'
 
1081
 
 
1082
    GCConst (operation, value, consttype)
 
1083
 
 
1084
  return result
 
1085
 
 
1086
 
 
1087
# =============================================================================
 
1088
# Combine two conditions with an AND clause. Side-effect: cond1 will be changed
 
1089
# =============================================================================
 
1090
 
 
1091
def combineConditions (cond1, cond2):
 
1092
  """
 
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.
 
1099
  """
 
1100
  # check for the trivial cases
 
1101
  if cond1 is None or cond1 == {}:
 
1102
    return cond2
 
1103
 
 
1104
  elif cond2 is None or cond2 == {}:
 
1105
    return cond1
 
1106
 
 
1107
  # make sure both parts are condition trees
 
1108
  if isinstance (cond1, types.DictType):
 
1109
    cond1 = buildConditionFromDict (cond1)
 
1110
 
 
1111
  if isinstance (cond2, types.DictType):
 
1112
    cond2 = buildConditionFromDict (cond2)
 
1113
 
 
1114
  # if the master condition has no elements, assign the second condition's
 
1115
  # children
 
1116
  if not len (cond1._children):
 
1117
    cond1._children = cond2._children
 
1118
    update = cond1
 
1119
 
 
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
 
1128
 
 
1129
    else:
 
1130
      parent = cond1._children [0]
 
1131
 
 
1132
    # determine where to start in the second condition tree
 
1133
    buddy = cond2
 
1134
    while len (buddy._children) == 1 and \
 
1135
          isinstance (buddy._children [0], GCand):
 
1136
      buddy = buddy._children [0]
 
1137
 
 
1138
    # and append all of the buddy's children to the top-level-and
 
1139
    parent._children.extend (buddy._children)
 
1140
 
 
1141
    update = parent
 
1142
  else:
 
1143
    update = None
 
1144
 
 
1145
  if update is not None:
 
1146
    for child in update._children:
 
1147
      child._parent = update
 
1148
 
 
1149
  return cond1
 
1150
 
 
1151
 
 
1152
# =============================================================================
 
1153
# Unify all elements in values to the same type
 
1154
# =============================================================================
 
1155
 
 
1156
def unify (values):
 
1157
  """
 
1158
  This function converts all values in the sequence @values to the same types
 
1159
  pushing the results into the sequence @result.
 
1160
  """
 
1161
  result = []
 
1162
  __unify (values, result)
 
1163
  return result
 
1164
 
 
1165
# -----------------------------------------------------------------------------
 
1166
# Actual working method for unification
 
1167
# -----------------------------------------------------------------------------
 
1168
def __unify (values, result):
 
1169
  """
 
1170
  This function does the dirty work of 'unify ()'.
 
1171
  """
 
1172
 
 
1173
  checktype (values, types.ListType)
 
1174
 
 
1175
  if not len (values):
 
1176
    return
 
1177
 
 
1178
  elif len (values) == 1:
 
1179
    result.append (values [0])
 
1180
    return
 
1181
 
 
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])
 
1186
 
 
1187
  v1 = values [0]
 
1188
  v2 = values [1]
 
1189
 
 
1190
  if v1 is None or v2 is None:
 
1191
    result.append (None)
 
1192
    values.remove (None)
 
1193
    __unify (values, result)
 
1194
 
 
1195
  elif type (v1) == type (v2):
 
1196
    result.append (v1)
 
1197
    values.remove (v1)
 
1198
    __unify (values, result)
 
1199
 
 
1200
  else:
 
1201
    # String-Conversions
 
1202
    if isinstance (v1, types.UnicodeType) or isinstance (v2, types.UnicodeType):
 
1203
      if isinstance (v1, types.UnicodeType):
 
1204
        oldValue = v1
 
1205
        chkValue = v2
 
1206
      else:
 
1207
        oldValue = v2
 
1208
        chkValue = v1
 
1209
 
 
1210
      # String to Boolean
 
1211
      if hasattr (types, "BooleanType") and \
 
1212
         isinstance (chkValue, types.BooleanType):
 
1213
        if oldValue.upper () in ['TRUE', 'T']:
 
1214
          newValue = True
 
1215
        elif oldValue.upper () in ['FALSE', 'F']:
 
1216
          newValue = False
 
1217
        else:
 
1218
          raise ConversionError, (oldValue, chkValue)
 
1219
  
 
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):
 
1224
        try:
 
1225
          if oldValue.upper () in ['TRUE', 'T']:
 
1226
            newValue = 1
 
1227
          elif oldValue.upper () in ['FALSE', 'F']:
 
1228
            newValue = 0
 
1229
          else:
 
1230
            newValue = int (oldValue)
 
1231
 
 
1232
        except ValueError:
 
1233
 
 
1234
          try:
 
1235
            newValue = float (oldValue)
 
1236
  
 
1237
          except ValueError:
 
1238
            raise ConversionError, (oldValue, chkValue)
 
1239
  
 
1240
      # String to DateTime
 
1241
      elif isinstance (chkValue, mx.DateTime.DateTimeType) or \
 
1242
           isinstance (chkValue, mx.DateTime.DateTimeDeltaType):
 
1243
  
 
1244
        try:
 
1245
          newValue = mx.DateTime.Parser.DateTimeFromString (oldValue)
 
1246
 
 
1247
        except ValueError:
 
1248
          raise ConversionError, (oldValue, chkValue)
 
1249
 
 
1250
      else:
 
1251
        raise ConversionRuleError, (oldValue, chkValue)
 
1252
  
 
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):
 
1258
        oldValue = v1
 
1259
        chkValue = v2
 
1260
      else:
 
1261
        oldValue = v2
 
1262
        chkValue = v1
 
1263
 
 
1264
      # Boolean to Integer
 
1265
      if isinstance (chkValue, types.IntType):
 
1266
        if oldValue:
 
1267
          newValue = 1
 
1268
        else:
 
1269
          newValue = 0
 
1270
  
 
1271
      # Boolean to Long
 
1272
      elif isinstance (chkValue, types.LongType):
 
1273
        if oldValue:
 
1274
          newValue = 1L
 
1275
        else:
 
1276
          newValue = 0L
 
1277
  
 
1278
      else:
 
1279
        raise ConversionRuleError, (oldValue, chkValue)
 
1280
  
 
1281
    # Integer conversions
 
1282
    elif isinstance (v1, types.IntType) or isinstance (v2, types.IntType):
 
1283
      if isinstance (v1, types.IntType):
 
1284
        oldValue = v1
 
1285
        chkValue = v2
 
1286
      else:
 
1287
        oldValue = v2
 
1288
        chkValue = v1
 
1289
  
 
1290
      # Integer to Float
 
1291
      if isinstance (chkValue, types.FloatType):
 
1292
        newValue = float (oldValue)
 
1293
 
 
1294
      elif isinstance (chkValue, types.LongType):
 
1295
        newValue = long (oldValue)
 
1296
  
 
1297
      else:
 
1298
        raise ConversionRuleError, (oldValue, chkValue)
 
1299
  
 
1300
    # Long conversions
 
1301
    elif isinstance (v1, types.LongType) or isinstance (v2, types.LongType):
 
1302
      if isinstance (v1, types.LongType):
 
1303
        oldValue = v1
 
1304
        chkValue = v2
 
1305
      else:
 
1306
        oldValue = v2
 
1307
        chkValue = v1
 
1308
  
 
1309
      # Long into Float
 
1310
      if isinstance (chkValue, types.FloatType):
 
1311
        newValue = float (oldValue)
 
1312
      else:
 
1313
        raise ConversionRuleError, (oldValue, chkValue)
 
1314
  
 
1315
    else:
 
1316
      raise ConversionRuleError, (v1, v2)
 
1317
  
 
1318
    values [oldValue == v2] = newValue
 
1319
    __unify (values, result)
 
1320
 
 
1321
 
 
1322
# =============================================================================
 
1323
# Definition of an impossible condition
 
1324
# =============================================================================
 
1325
 
 
1326
GCimpossible = buildTreeFromList (['eq', ['const', 1], ['const', 2]])
 
1327
 
 
1328
 
 
1329
 
 
1330
###############################################################################
 
1331
###############################################################################
 
1332
####                        Depreciated Methods                              ##
 
1333
###############################################################################
 
1334
###############################################################################
 
1335
 
 
1336
#
 
1337
# a table with extra information about the different condition types
 
1338
#  (needed for condition tree <-> prefix table conversion)
 
1339
#
 
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),
 
1361
  }
 
1362
 
 
1363
# creates an GCondition Tree out of an list of tokens in a prefix
 
1364
# order.
 
1365
 
 
1366
def buildTreeFromPrefix(term):
 
1367
 
 
1368
  # create a GCondition object as top object in the object stack
 
1369
  parent={0:(GCondition())}
 
1370
 
 
1371
  # GCondition will have only one parameter
 
1372
  # add paramcount=1 to the parameter count stack
 
1373
  paramcount={0:1}
 
1374
 
 
1375
  # set start level for stack to zero
 
1376
  level=0
 
1377
  for i in term:
 
1378
 
 
1379
    # convert type into an object
 
1380
    if conditionElements.has_key(i[0]):
 
1381
      e=conditionElements[i[0]][2](parent[level])
 
1382
      level=level+1
 
1383
      # get parameter count
 
1384
      paramcount[level]=conditionElements[i[0]][0]
 
1385
      parent[level]=e
 
1386
    elif i[0]=="field":
 
1387
      e=GCField(parent[level], i[1])
 
1388
      paramcount[level]=paramcount[level]-1
 
1389
      if paramcount[level]==0:
 
1390
        level=level-1
 
1391
    elif i[0]=="const":
 
1392
      e=GCConst(parent[level], i[1])
 
1393
      paramcount.update({level:(paramcount[level]-1)})
 
1394
      if paramcount[level]==0:
 
1395
        level=level-1
 
1396
#    print "NAME: %s  VALUE: %s  LEVEL: %s PCOUNT: %s" % \
 
1397
#          (i[0],i[1],level,paramcount[level])
 
1398
 
 
1399
  return parent[0];
 
1400
 
 
1401
 
 
1402
 
 
1403
def buildPrefixFromTree(conditionTree):
 
1404
  if type(conditionTree) != types.InstanceType:
 
1405
    tmsg = u_("No valid condition tree")
 
1406
    raise ConditionError, tmsg
 
1407
  else:
 
1408
    otype = string.lower(conditionTree._type[2:])
 
1409
 
 
1410
    #
 
1411
    #  care for objects without children
 
1412
    #
 
1413
    if otype == 'cfield':
 
1414
      return [('field',"%s" % conditionTree.name)]
 
1415
 
 
1416
    elif otype == 'cconst':
 
1417
      return [('const',conditionTree.value)]
 
1418
 
 
1419
    elif otype == 'cparam':
 
1420
      return [('const', conditionTree.getValue())]
 
1421
 
 
1422
    #
 
1423
    #  if its an conditional object, then process it's children
 
1424
    #
 
1425
    elif conditionElements.has_key(otype):
 
1426
      result=[]
 
1427
 
 
1428
      # first add operator to the list
 
1429
      result.append((otype,''));  #  ,None));
 
1430
 
 
1431
 
 
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,''));
 
1441
 
 
1442
 
 
1443
      # then add children
 
1444
      for i in range(0, len(conditionTree._children)):
 
1445
        result = result + \
 
1446
                 buildPrefixFromTree(conditionTree._children[i])
 
1447
 
 
1448
      #
 
1449
      #  check for integrity of condition
 
1450
      #
 
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
 
1458
 
 
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
 
1466
 
 
1467
 
 
1468
      # return combination
 
1469
      return result;
 
1470
 
 
1471
    else:
 
1472
      tmsg = u_('Condition clause "%s" is not supported '
 
1473
                'by the condition to prefix table conversion.') % otype
 
1474
      raise ConditionNotSupported, tmsg
 
1475
 
 
1476
#
 
1477
# Combine two conditions with an and clause.
 
1478
# NOTE: This modifies cond1 (and also returns it)
 
1479
#
 
1480
def __Original__combineConditions (cond1, cond2):
 
1481
  if cond1 == None or cond1 == {}:
 
1482
    return cond2
 
1483
  elif cond2 == None or cond2 == {}:
 
1484
    return cond1
 
1485
 
 
1486
  if type(cond1) == type({}):
 
1487
    cond1 = buildConditionFromDict(cond1)
 
1488
  if type(cond2) == type({}):
 
1489
    cond2 = buildConditionFromDict(cond2)
 
1490
 
 
1491
  if not len(cond1._children):
 
1492
    cond1._children = cond2._children
 
1493
    return cond1
 
1494
  elif len(cond2._children):
 
1495
    children = cond1._children[:]
 
1496
    cond1._children = []
 
1497
    _and = GCand(cond1)
 
1498
    _and._children = children
 
1499
    if len(cond2._children) > 1:
 
1500
      _and2 = GCand(cond1)
 
1501
      _and2._children = cond2._children[:]
 
1502
    else:
 
1503
      cond1._children.append(cond2._children[0])
 
1504
 
 
1505
  return cond1
 
1506
 
 
1507
 
 
1508
 
 
1509
# =============================================================================
 
1510
# Module self test code
 
1511
# =============================================================================
 
1512
 
 
1513
if __name__ == '__main__':
 
1514
  import sys
 
1515
  from mx.DateTime import *
 
1516
 
 
1517
  # ---------------------------------------------------------------------------
 
1518
  # self-test-function: unify two arguments
 
1519
  # ---------------------------------------------------------------------------
 
1520
 
 
1521
  def _check_unify (values):
 
1522
    args = string.join (["%s (%s)" % (v, type (v).__name__) for v in values])
 
1523
    print "\nUnify:", args
 
1524
 
 
1525
    try:
 
1526
      res = string.join (["%s (%s)" % (r, type (r).__name__) \
 
1527
                          for r in unify (values)])
 
1528
      print "  RES=", res
 
1529
 
 
1530
    except:
 
1531
      print sys.exc_info () [0], sys.exc_info () [1]
 
1532
 
 
1533
 
 
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'])
 
1542
  _check_unify ([])
 
1543
  _check_unify ([1])
 
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'])
 
1549
 
 
1550
  print "unification sequence complete"
 
1551
  raw_input ()
 
1552
 
 
1553
 
 
1554
  # ---------------------------------------------------------------------------
 
1555
  # Self-test-function: Construct conditions and evaluate them
 
1556
  # ---------------------------------------------------------------------------
 
1557
 
 
1558
  def _check_construction (source, lookup):
 
1559
    if isinstance (source, types.DictType):
 
1560
      cond = buildConditionFromDict (source)
 
1561
    else:
 
1562
      cond = buildTreeFromList (source)
 
1563
 
 
1564
    print
 
1565
    print "Source:", source
 
1566
    print "Prefix:", cond.prefixNotation ()
 
1567
    print "Lookup:", lookup
 
1568
    print "Eval  :", cond.evaluate (lookup)
 
1569
 
 
1570
 
 
1571
  print "Condition tree transformation test"
 
1572
 
 
1573
  prefix = ['and', ['eq', ['field', u'foo'], ['const', 'bar']],
 
1574
                   ['ne', ['field', u'bar'], ['const', 'foobar']]]
 
1575
 
 
1576
  lookup = {'foo': 'bar',
 
1577
            'bar': 'foobarX'}
 
1578
  _check_construction (prefix, lookup)
 
1579
 
 
1580
 
 
1581
  prefix = ['or', ['between', ['field', u'foo'],
 
1582
                        ['const', 'bar'], ['const', 'char']],
 
1583
            ['not', ['null', ['field', u'price']]]]
 
1584
 
 
1585
  lookup = {'foo': 'capo', 'price': None}
 
1586
  _check_construction (prefix, lookup)
 
1587
 
 
1588
  prefix = ['like', ['field', u'foo'], ['const', 'bar%']]
 
1589
  cond = buildTreeFromList (prefix)
 
1590
  lookup = {'foo': 'Barrel'}
 
1591
  _check_construction (prefix, lookup)
 
1592
 
 
1593
  lookup = {'foo': 'bar is running'}
 
1594
  _check_construction (prefix, lookup)
 
1595
 
 
1596
  source = {'foo': 'bar', 'bar': 'trash'}
 
1597
  lookup = {'foo': 'bar', 'bar': 'trash'}
 
1598
  _check_construction (source, lookup)
 
1599
 
 
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)
 
1604
 
 
1605
  prefix = ['eq', ['upper', ['field', 'nfoo']], ['upper', ['const', 'baR']]]
 
1606
  lookup = {'nfoo': 'bAr'}
 
1607
  _check_construction (prefix, lookup)
 
1608
 
 
1609
  prefix = ['eq', ['lower', ['field', 'nfoo']], ['const', 'bar']]
 
1610
  lookup = {'nfoo': 'BaR'}
 
1611
  _check_construction (prefix, lookup)
 
1612
 
 
1613
  print "end of construction test sequence"
 
1614
  raw_input ()
 
1615
 
 
1616
  # ---------------------------------------------------------------------------
 
1617
  # Self-test-function: Combine and evaluate condition trees
 
1618
  # ---------------------------------------------------------------------------
 
1619
 
 
1620
  def _check_combineConditions (cond1, cond2):
 
1621
    rc1 = cond1
 
1622
    rc2 = cond2
 
1623
    if not isinstance (cond1, types.DictType):
 
1624
      rc1 = cond1.prefixNotation ()
 
1625
    if not isinstance (cond2, types.DictType):
 
1626
      rc2 = cond2.prefixNotation ()
 
1627
 
 
1628
    print "\nCombination of:"
 
1629
    print "     a:", rc1
 
1630
    print "     b:", rc2
 
1631
    res = combineConditions (cond1, cond2)
 
1632
    print "   RES=", res.prefixNotation ()
 
1633
 
 
1634
 
 
1635
  cond1 = GCondition ()
 
1636
  cond2 = GCondition ()
 
1637
 
 
1638
  _check_combineConditions (cond1, cond2)
 
1639
 
 
1640
  prefix = ['eq', ['field', 'foo'], ['const', 'bar']]
 
1641
  cond2 = buildTreeFromList (prefix)
 
1642
 
 
1643
  _check_combineConditions (cond1, cond2)
 
1644
 
 
1645
  cond1 = buildTreeFromList (prefix)
 
1646
  cond2 = GCondition ()
 
1647
  _check_combineConditions (cond1, cond2)
 
1648
 
 
1649
  prefix2 = ['and', ['null', ['field', 'nfoo']],
 
1650
                    ['notnull', ['field', 'nbar']]]
 
1651
  cond2 = buildTreeFromList (prefix2)
 
1652
  _check_combineConditions (cond1, cond2)
 
1653
 
 
1654
  cond1 = cond2
 
1655
  cond2 = buildTreeFromList (prefix)
 
1656
  _check_combineConditions (cond1, cond2)
 
1657
 
 
1658
  prefix3 = ['null', ['field', 'nfoo']]
 
1659
  cond1 = buildTreeFromList (prefix)
 
1660
  cond2 = buildTreeFromList (prefix3)
 
1661
  _check_combineConditions (cond1, cond2)
 
1662
 
 
1663
  prefix4 = [['ne', ['field', 'foo'], ['const', 'bar']],
 
1664
             ['lt', ['field', 'price'], ['const', 150.0]]]
 
1665
  cond2 = buildTreeFromList (prefix4)
 
1666
  _check_combineConditions (cond1, cond2)
 
1667
 
 
1668
  cond1 = buildTreeFromList (prefix)
 
1669
  _check_combineConditions (cond2, cond1)
 
1670
 
 
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)
 
1676
 
 
1677
  cond1 = buildTreeFromList (['and'])
 
1678
  cond2 = buildTreeFromList (prefix)
 
1679
  _check_combineConditions (cond1, cond2)
 
1680
 
 
1681
  print "start off"
 
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
 
1687
    print "Children:"
 
1688
    for c in i._children:
 
1689
      print "C:", c.prefixNotation ()
 
1690
  
 
1691
  print "\n\nImpossible condition:", GCimpossible.prefixNotation ()