]> SALOME platform Git repositories - tools/eficas.git/blob - Noyau/N_VALIDATOR.py
Salome HOME
merge de la branche BR_dev_mars_06 (tag V1_10b5) dans la branche principale
[tools/eficas.git] / Noyau / N_VALIDATOR.py
1 #@ MODIF N_VALIDATOR Noyau  DATE 16/05/2006   AUTEUR DURAND C.DURAND 
2 # -*- coding: iso-8859-1 -*-
3 #            CONFIGURATION MANAGEMENT OF EDF VERSION
4 # ======================================================================
5 # COPYRIGHT (C) 1991 - 2003  EDF R&D                  WWW.CODE-ASTER.ORG
6 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY  
7 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY  
8 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR     
9 # (AT YOUR OPTION) ANY LATER VERSION.                                                  
10 #                                                                       
11 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT   
12 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF            
13 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU      
14 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.                              
15 #                                                                       
16 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE     
17 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,         
18 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.        
19 # ======================================================================
20
21 """
22    Ce module contient toutes les classes necessaires pour
23    implanter le concept de validateur dans Accas
24 """
25 import types
26 import string
27 import traceback
28
29 class ValError(Exception):pass
30
31 def cls_mro(cls):
32     if hasattr(cls,"__mro__"):return cls.__mro__
33     mro=[cls]
34     for base in cls.__bases__:
35         mro.extend(cls_mro(base))
36     return mro
37
38 class Protocol:
39     def __init__(self,name):
40         self.registry = {}
41         self.name = name
42         self.args={}
43
44     def register(self, T, A):
45         self.registry[T] = A
46
47     def adapt(self, obj):
48         # (a) verifier si l'objet peut s'adapter au protocole
49         adapt = getattr(obj, '__adapt__', None)
50         if adapt is not None:
51             # on demande à l'objet obj de réaliser lui-meme l'adaptation
52             return adapt(self)
53
54         # (b) verifier si un adapteur est enregistré (si oui l'utiliser)
55         if self.registry:
56             for T in cls_mro(obj.__class__):
57                 if T in self.registry:
58                     return self.registry[T](obj,self,**self.args)
59
60         # (c) utiliser l'adapteur par defaut
61         return self.default(obj,**self.args)
62
63     def default(self,obj,**args):
64         raise TypeError("Can't adapt %s to %s" %
65                         (obj.__class__.__name__, self.name))
66
67 class PProtocol(Protocol):
68     """Verificateur de protocole paramétré (classe de base)"""
69     #Protocole paramétré. Le registre est unique pour toutes les instances. La methode register est une methode de classe
70     registry={}
71     def __init__(self,name,**args):
72         self.name = name
73         self.args=args
74     def register(cls, T, A):
75         cls.registry[T] = A
76     register=classmethod(register)
77
78 class ListProtocol(Protocol):
79     """Verificateur de protocole liste : convertit un objet quelconque en liste pour validation ultérieure"""
80     def default(self,obj):
81         if type(obj) == types.TupleType :
82             if obj[0] in ('RI','MP'):
83                 #il s'agit d'un complexe ancienne mode. La cardinalite vaut 1
84                 return (obj,)
85             else:
86                 return obj
87         elif type(obj) == types.ListType :
88             return obj
89         elif obj == None :
90             # pas de valeur affecte. La cardinalite vaut 0
91             return obj
92         elif type(obj) == types.StringType :
93             #il s'agit d'une chaine. La cardinalite vaut 1
94             return (obj,)
95         else:
96             try:
97                 # si l'objet supporte len, on a la cardinalite
98                 length=len(obj)
99                 return obj
100             except:
101                 # sinon elle vaut 1
102                 return (obj,)
103
104 listProto=ListProtocol("list")
105
106 class TypeProtocol(PProtocol):
107     """Verificateur de type parmi une liste de types possibles"""
108     #pas de registre par instance. Registre unique pour toutes les instances de TypeProtocol
109     registry={}
110     def __init__(self,name,typ=None):
111         PProtocol.__init__(self,name,typ=typ)
112         self.typ=typ
113
114     def default(self,obj,typ):
115         for type_permis in typ:
116             if type_permis == 'R':
117                 if type(obj) in (types.IntType,types.FloatType,types.LongType):return obj
118             elif type_permis == 'I':
119                 if type(obj) in (types.IntType,types.LongType):return obj
120             elif type_permis == 'C':
121                 if self.is_complexe(obj):return obj
122             elif type_permis == 'TXM':
123                 if type(obj)==types.StringType:return obj
124             elif type_permis == 'shell':
125                 if type(obj)==types.StringType:return obj
126             elif type(type_permis) == types.ClassType:
127                 if self.is_object_from(obj,type_permis):return obj
128             elif type(type_permis) == types.InstanceType:
129                 try:
130                     if type_permis.__convert__(obj) : return obj
131                 except:
132                     pass
133             else:
134                 print "Type non encore géré %s" %`type_permis`
135
136         raise ValError("%s n'est pas d'un type autorisé: %s" % (repr(obj),typ))
137
138     def is_complexe(self,valeur):
139         """ Retourne 1 si valeur est un complexe, 0 sinon """
140         if type(valeur) in (types.ComplexType,types.IntType,types.FloatType,types.LongType):
141             # Pour permettre l'utilisation de complexes Python
142             return 1
143         elif type(valeur) != types.TupleType :
144             # On n'autorise pas les listes pour les complexes
145             return 0
146         elif len(valeur) != 3:
147             return 0
148         else:
149             # Un complexe doit etre un tuple de longueur 3 avec 'RI' ou 'MP' comme premiere
150             # valeur suivie de 2 reels.
151             if string.strip(valeur[0]) in ('RI','MP'):
152                 try:
153                     v1=reelProto.adapt(valeur[1]),reelProto.adapt(valeur[2])
154                     return 1
155                 except:
156                     return 0
157             else:
158                 return 0
159
160     def is_object_from(self,objet,classe):
161         """
162            Retourne 1 si objet est une instance de la classe classe, 0 sinon
163         """
164         convert = getattr(classe, '__convert__', None)
165         if convert is not None:
166             # classe verifie les valeurs
167             try:
168                 v=  convert(objet)
169                 return v is not None
170             except:
171                 return 0
172         # On accepte les instances de la classe et des classes derivees
173         return type(objet) == types.InstanceType and isinstance(objet,classe)
174
175 reelProto=TypeProtocol("reel",typ=('R',))
176
177 class CardProtocol(PProtocol):
178     """Verificateur de cardinalité """
179     #pas de registre par instance. Registre unique pour toutes les instances
180     registry={}
181     def __init__(self,name,min=1,max=1):
182         PProtocol.__init__(self,name,min=min,max=max)
183
184     def default(self,obj,min,max):
185         length=len(obj)
186         if length < min or length >max:
187             raise ValError("Nombre d'arguments de %s incorrect (min = %s, max = %s)" % (repr(obj),min,max) )
188         return obj
189
190 class IntoProtocol(PProtocol):
191     """Verificateur de choix possibles : liste discrète ou intervalle"""
192     #pas de registre par instance. Registre unique pour toutes les instances
193     registry={}
194     def __init__(self,name,into=None,val_min='**',val_max='**'):
195         PProtocol.__init__(self,name,into=into,val_min=val_min,val_max=val_max)
196         self.val_min=val_min
197         self.val_max=val_max
198
199     def default(self,obj,into,val_min,val_max):
200         if into:
201             if obj not in into:
202                 raise ValError("La valeur : %s  ne fait pas partie des choix possibles %s" % (repr(obj),into) )
203         else:
204             #on est dans le cas d'un ensemble continu de valeurs possibles (intervalle)
205             if type(obj) in (types.IntType,types.FloatType,types.LongType) :
206                 if val_min == '**': val_min = obj -1
207                 if val_max == '**': val_max = obj +1
208                 if obj < val_min or obj > val_max :
209                     raise ValError("La valeur : %s est en dehors du domaine de validité [ %s , %s ]" % (repr(obj),self.val_min,self.val_max) )
210         return obj
211
212 class MinStr:
213     #exemple de classe pour verificateur de type
214     #on utilise des instances de classe comme type (typ=MinStr(3,6), par exemple)
215     def __init__(self,min,max):
216         self.min=min
217         self.max=max
218
219     def __convert__(self,valeur):
220         if type(valeur) == types.StringType and self.min <= len(valeur) <= self.max:return valeur
221         raise ValError("%s n'est pas une chaine de longueur comprise entre %s et %s" % (valeur,self.min,self.max))
222
223     def __repr__(self):
224         return "TXM de longueur entre %s et %s" %(self.min,self.max)
225
226 class Valid(PProtocol):
227    """
228         Cette classe est la classe mere des validateurs Accas
229         Elle doit etre derivee 
230         Elle presente la signature des methodes indispensables pour son bon 
231         fonctionnement et dans certains cas leur comportement par défaut.
232
233         @ivar cata_info: raison de la validite ou de l'invalidite du validateur meme
234         @type cata_info: C{string}
235    """
236    registry={}
237    def __init__(self,**args):
238        PProtocol.__init__(self,"valid",**args)
239
240    def info(self):
241        """
242           Cette methode retourne une chaine de caractères informative sur
243           la validation demandée par le validateur. Elle est utilisée
244           pour produire le compte-rendu de validité du mot clé associé.
245        """
246        return "valeur valide" 
247
248    def aide(self):
249        """
250           Cette methode retourne une chaine de caractère qui permet 
251           de construire un message d'aide en ligne.
252           En général, le message retourné est le meme que celui retourné par la
253           méthode info.
254        """
255        return self.info()
256
257    def info_erreur_item(self):
258        """
259           Cette méthode permet d'avoir un message d'erreur pour un item
260           dans une liste dans le cas ou le validateur fait des vérifications
261           sur les items d'une liste. Si le validateur fait seulement des
262           vérifications sur la liste elle meme et non sur ses items, la méthode
263           doit retourner une chaine vide.
264        """
265        return " "
266
267    def info_erreur_liste(self):
268        """
269           Cette méthode a un comportement complémentaire de celui de
270           info_erreur_item. Elle retourne un message d'erreur lié uniquement
271           aux vérifications sur la liste elle meme et pas sur ses items.
272           Dans le cas où le validateur ne fait pas de vérification sur des
273           listes, elle retourne une chaine vide
274        """
275        return " "
276
277    def verif(self,valeur):
278        """
279            Cette methode sert a verifier si la valeur passee en argument est consideree
280            comme valide ou non par le validateur. Dans le premier cas le validateur retourne 1
281            (valide) sinon 0 (invalide).
282
283            @type valeur: tout type python
284            @param valeur: valeur du mot cle a valider
285            @rtype: C{boolean}
286            @return: indicateur de validite 1 (valide) ou 0 (invalide)
287        """
288        raise "Must be implemented"
289    
290    def verif_item(self,valeur):
291        """
292           La methode verif du validateur effectue une validation complete de
293           la valeur. valeur peut etre un scalaire ou une liste. Le validateur
294           doit traiter les 2 aspects s'il accepte des listes (dans ce cas la
295           methode is_list doit retourner 1).
296           La methode valid_item sert pour effectuer des validations partielles
297           de liste. Elle doit uniquement verifier la validite d'un item de
298           liste mais pas les caracteristiques de la liste.
299        """
300        return 0
301
302    def valide_liste_partielle(self,liste_courante):
303        """
304           Cette methode retourne un entier qui indique si liste_courante est partiellement valide (valeur 1)
305           ou invalide (valeur 0). La validation partielle concerne les listes en cours de construction : on
306           veut savoir si la liste en construction peut etre complétée ou si elle peut déjà etre considérée
307           comme invalide.
308           En général un validateur effectue la meme validation pour les listes partielles et les
309           listes complètes.
310        """
311        return self.verif(liste_courante)
312
313    def verif_cata(self):
314        """
315            Cette methode sert a realiser des verifications du validateur lui meme.
316            Elle est facultative et retourne 1 (valide) par defaut.
317            Elle retourne 0 si le validateur est lui meme invalide si par exemple ses
318            parametres de definition ne sont pas corrects.
319            La raison de l'invalidite est stockee dans l'attribut cata_info.
320
321            @rtype: C{boolean}
322            @return: indicateur de validite 1 (valide) ou 0 (invalide)
323        """
324        return 1
325
326    def is_list(self):
327        """
328           Cette méthode retourne un entier qui indique si le validateur
329           permet les listes (valeur 1) ou ne les permet pas (valeur 0).
330           Par défaut, un validateur n'autorise que des scalaires.
331        """
332        return 0
333
334    def has_into(self):
335        """
336           Cette méthode retourne un entier qui indique si le validateur
337           propose une liste de choix (valeur 1) ou n'en propose pas.
338           Par défaut, un validateur n'en propose pas.
339        """
340        return 0
341
342    def get_into(self,liste_courante=None,into_courant=None):
343        """
344           Cette méthode retourne la liste de choix proposée par le validateur.
345           Si le validateur ne propose pas de liste de choix, la méthode
346           retourne None.
347           L'argument d'entrée liste_courante, s'il est différent de None, donne
348           la liste des choix déjà effectués par l'utilisateur. Dans ce cas, la
349           méthode get_into doit calculer la liste des choix en en tenant
350           compte. Par exemple, si le validateur n'autorise pas les répétitions,
351           la liste des choix retournée ne doit pas contenir les choix déjà
352           contenus dans liste_courante.
353           L'argument d'entrée into_courant, s'il est différent de None, donne
354           la liste des choix proposés par d'autres validateurs. Dans ce cas,
355           la méthode get_into doit calculer la liste des choix à retourner
356           en se limitant à cette liste initiale. Par exemple, si into_courant
357           vaut (1,2,3) et que le validateur propose la liste de choix (3,4,5),
358           la méthode ne doit retourner que (3,).
359
360           La méthode get_into peut retourner une liste vide [], ce qui veut
361           dire qu'il n'y a pas (ou plus) de choix possible. Cette situation
362           peut etre normale : l''utilisateur a utilisé tous les choix, ou
363           résulter d'une incohérence des validateurs :
364           choix parmi (1,2,3) ET choix parmi (4,5,6). Il est impossible de
365           faire la différence entre ces deux situations.
366        """
367        return into_courant
368
369 class ListVal(Valid):
370    """
371        Cette classe sert de classe mère pour tous les validateurs qui acceptent
372        des listes.
373    """
374    def is_list(self):
375        return 1
376
377    def get_into(self,liste_courante=None,into_courant=None):
378        """
379           Cette méthode get_into effectue un traitement général qui consiste
380           a filtrer la liste de choix into_courant, si elle existe, en ne
381           conservant que les valeurs valides (appel de la méthode valid).
382        """
383        if into_courant is None:
384           return None
385        else:
386           liste_choix=[]
387           for e in into_courant:
388               if self.verif(e):
389                  liste_choix.append(e)
390           return liste_choix
391
392    def convert(self,valeur):
393        """
394           Méthode convert pour les validateurs de listes. Cette méthode
395           fait appel à la méthode convert_item sur chaque élément de la
396           liste.
397        """
398        if type(valeur) in (types.ListType,types.TupleType):
399           for val in valeur:
400               self.convert_item(val)
401           return valeur
402        else:
403           return self.convert_item(valeur)
404
405    def verif(self,valeur):
406        """
407           Méthode verif pour les validateurs de listes. Cette méthode
408           fait appel à la méthode verif_item sur chaque élément de la 
409           liste. Si valeur est un paramètre, on utilise sa valeur effective
410           valeur.valeur.
411        """
412        if type(valeur) in (types.ListType,types.TupleType):
413           for val in valeur:
414               if not self.verif_item(val):
415                  return 0
416           return 1
417        else:
418           return self.verif_item(valeur)
419
420 class Compulsory(ListVal):
421       """
422           Validateur operationnel
423           Verification de la présence obligatoire d'un élément dans une liste
424       """
425       registry={}
426       def __init__(self,elem=()):
427           if type(elem) not in (types.ListType,types.TupleType): elem=(elem,)
428           Valid.__init__(self,elem=elem)
429           self.elem=elem
430           self.cata_info=""
431
432       def info(self):
433           return "valeur %s obligatoire" % `self.elem`
434
435       def default(self,valeur,elem):
436           return valeur
437
438       def verif_item(self,valeur):
439           return 1
440
441       def convert(self,valeur):
442           elem=list(self.elem)
443           for val in valeur:
444               v=self.adapt(val)
445               if v in elem:elem.remove(v)
446           if elem:
447               raise ValError("%s ne contient pas les elements obligatoires : %s " %(valeur,elem))
448           return valeur
449
450       def has_into(self):
451           return 1
452
453       def verif(self,valeur):
454           if type(valeur) not in (types.ListType,types.TupleType):
455              liste=list(valeur)
456           else:
457              liste=valeur
458           for val in self.elem :
459              if val not in liste : return 0
460           return 1
461
462       def info_erreur_item(self):
463           return "La valeur n'est pas dans la liste des choix possibles"
464
465 class NoRepeat(ListVal):
466       """
467           Validateur operationnel
468           Verification d'absence de doublons dans la liste.
469       """
470       def __init__(self):
471           Valid.__init__(self)
472           self.cata_info=""
473
474       def info(self):
475           return ": pas de présence de doublon dans la liste"
476
477       def info_erreur_liste(self):
478           return "Les doublons ne sont pas permis"
479
480       def default(self,valeur):
481           if valeur in self.liste : raise ValError("%s est un doublon" % valeur)
482           return valeur
483
484       def convert(self,valeur):
485           self.liste=[]
486           for val in valeur:
487               v=self.adapt(val)
488               self.liste.append(v)
489           return valeur
490
491       def verif_item(self,valeur):
492           return 1
493
494       def verif(self,valeur):
495           if type(valeur) in (types.ListType,types.TupleType):
496              liste=list(valeur)
497              for val in liste:
498                 if liste.count(val)!=1 : return 0
499              return 1
500           else:
501              return 1
502
503       def get_into(self,liste_courante=None,into_courant=None):
504           """
505           Methode get_into spécifique pour validateur NoRepeat, on retourne
506           une liste de choix qui ne contient aucune valeur de into_courant
507           déjà contenue dans liste_courante
508           """
509           if into_courant is None:
510              liste_choix=None
511           else:
512              liste_choix=[]
513              for e in into_courant:
514                  if e in liste_choix: continue
515                  if liste_courante is not None and e in liste_courante: continue
516                  liste_choix.append(e)
517           return liste_choix
518
519 class LongStr(ListVal):
520       """
521           Validateur operationnel
522           Verification de la longueur d une chaine
523       """
524       def __init__(self,low,high):
525           ListVal.__init__(self,low=low,high=high)
526           self.low=low
527           self.high=high
528           self.cata_info=""
529
530       def info(self):
531           return "longueur de la chaine entre %s et %s" %(self.low,self.high)
532
533       def info_erreur_item(self):
534           return "Longueur de la chaine incorrecte"
535
536       def convert(self,valeur):
537           for val in valeur:
538               v=self.adapt(val)
539           return valeur
540
541       def verif_item(self,valeur):
542           try:
543              self.adapt(valeur)
544              return 1
545           except:
546              return 0
547
548       def default(self,valeur,low,high):
549           if type(valeur) != types.StringType :
550              raise ValError("%s n'est pas une string" % repr(valeur))
551           if valeur[0]=="'" and valeur[-1]=="'" :
552              low=low+2
553              high=high+2
554           if len(valeur) < low or len(valeur) > high :
555              raise ValError("%s n'est pas de la bonne longueur" % repr(valeur))
556           return valeur
557
558 class OrdList(ListVal):
559       """
560           Validateur operationnel
561           Verification qu'une liste est croissante ou decroissante
562       """
563       def __init__(self,ord):
564           ListVal.__init__(self,ord=ord)
565           self.ord=ord
566           self.cata_info=""
567
568       def info(self):
569           return "liste %s" % self.ord
570
571       def info_erreur_liste(self) :
572           return "La liste doit etre en ordre "+self.ord
573
574       def convert(self,valeur):
575           self.val=None
576           self.liste=valeur
577           for v in valeur:
578               self.adapt(v)
579           return valeur
580
581       def default(self,valeur,ord):
582           if self.ord=='croissant':
583               if self.val is not None and valeur<self.val:
584                   raise ValError("%s n'est pas par valeurs croissantes" % repr(self.liste))
585           elif self.ord=='decroissant':
586               if self.val is not None and valeur > self.val:
587                   raise ValError("%s n'est pas par valeurs decroissantes" % repr(self.liste))
588           self.val=valeur
589           return valeur
590
591       def verif_item(self,valeur):
592           return 1
593
594       def get_into(self,liste_courante=None,into_courant=None):
595           """
596           Methode get_into spécifique pour validateur OrdList, on retourne
597           une liste de choix qui ne contient aucune valeur de into_courant
598           dont la valeur est inférieure à la dernière valeur de
599           liste_courante, si elle est différente de None.
600           """
601           if into_courant is None:
602              return None
603           elif not liste_courante :
604              return into_courant
605           else:
606              liste_choix=[]
607              last_val=liste_choix[-1]
608              for e in into_courant:
609                  if self.ord=='croissant' and e <= last_val:continue
610                  if self.ord=='decroissant' and e >= last_val:continue
611                  liste_choix.append(e)
612              return liste_choix
613
614 class OrVal(Valid):
615       """
616           Validateur operationnel
617           Cette classe est un validateur qui controle une liste de validateurs
618           Elle verifie qu'au moins un des validateurs de la liste valide la valeur
619       """
620       def __init__(self,validators=()):
621           if type(validators) not in (types.ListType,types.TupleType):
622              validators=(validators,)
623           self.validators=[]
624           for validator in validators:
625               if type(validator) == types.FunctionType:
626                  self.validators.append(FunctionVal(validator))
627               else:
628                  self.validators.append(validator)
629           self.cata_info=""
630
631       def info(self):
632           return "\n ou ".join([v.info() for v in self.validators])
633
634       def convert(self,valeur):
635           for validator in self.validators:
636               try:
637                  return validator.convert(valeur)
638               except:
639                  pass
640           raise ValError("%s n'est pas du bon type" % repr(valeur))
641
642       def info_erreur_item(self):
643           l=[]
644           for v in self.validators:
645               err=v.info_erreur_item()
646               if err != " " : l.append(err)
647           chaine=" \n ou ".join(l)
648           return chaine
649
650       def info_erreur_liste(self):
651           l=[]
652           for v in self.validators:
653               err=v.info_erreur_liste()
654               if err != " " : l.append(err)
655           chaine=" \n ou ".join(l)
656           return chaine
657
658       def is_list(self):
659           """
660              Si plusieurs validateurs sont reliés par un OU
661              il suffit qu'un seul des validateurs attende une liste
662              pour qu'on considère que leur union attend une liste.
663           """
664           for validator in self.validators:
665               v=validator.is_list()
666               if v :
667                  return 1
668           return 0
669
670       def verif(self,valeur):
671           for validator in self.validators:
672               v=validator.verif(valeur)
673               if v :
674                  return 1
675           return 0
676
677       def verif_item(self,valeur):
678           for validator in self.validators:
679               v=validator.verif_item(valeur)
680               if v :
681                  return 1
682           return 0
683
684       def verif_cata(self):
685           infos=[]
686           for validator in self.validators:
687               v=validator.verif_cata()
688               if not v :infos.append(validator.cata_info)
689           if infos:
690              self.cata_info="\n".join(infos)
691              return 0
692           self.cata_info=""
693           return 1
694
695       def has_into(self):
696           """
697           Dans le cas ou plusieurs validateurs sont reliés par un OU
698           il faut que tous les validateurs proposent un choix pour
699           qu'on considère que leur union propose un choix.
700           Exemple : Enum(1,2,3) OU entier pair, ne propose pas de choix
701           En revanche, Enum(1,2,3) OU Enum(4,5,6) propose un choix (1,2,3,4,5,6)
702           """
703           for validator in self.validators:
704               v=validator.has_into()
705               if not v :
706                  return 0
707           return 1
708
709       def get_into(self,liste_courante=None,into_courant=None):
710           """
711           Dans le cas ou plusieurs validateurs sont reliés par un OU
712           tous les validateurs doivent proposer un choix pour
713           qu'on considère que leur union propose un choix. Tous les choix
714           proposés par les validateurs sont réunis (opérateur d'union).
715           Exemple : Enum(1,2,3) OU entier pair, ne propose pas de choix
716           En revanche, Enum(1,2,3) OU Enum(4,5,6) propose un 
717           choix (1,2,3,4,5,6)       
718           """
719           validator_into=[]
720           for validator in self.validators:
721               v_into=validator.get_into(liste_courante,into_courant)
722               if v_into is None:
723                  return v_into
724               validator_into.extend(v_into)
725           return validator_into
726
727       def valide_liste_partielle(self,liste_courante=None):
728           """
729            Méthode de validation de liste partielle pour le validateur Or.
730            Si un des validateurs gérés par le validateur Or considère la
731            liste comme valide, le validateur Or la considère comme valide.
732           """
733           for validator in self.validators:
734               v=validator.valide_liste_partielle(liste_courante)
735               if v :
736                  return 1
737           return 0
738
739 class AndVal(Valid):
740       """
741           Validateur operationnel
742           Cette classe est un validateur qui controle une liste de validateurs
743           Elle verifie que tous les validateurs de la liste valident la valeur
744       """
745       def __init__(self,validators=()):
746           if type(validators) not in (types.ListType,types.TupleType):
747              validators=(validators,)
748           self.validators=[]
749           for validator in validators:
750               if type(validator) == types.FunctionType:
751                  self.validators.append(FunctionVal(validator))
752               else:
753                  self.validators.append(validator)
754           self.cata_info=""
755
756       def info(self):
757           return "\n et ".join([v.info() for v in self.validators])
758
759       def convert(self,valeur):
760           for validator in self.validators:
761               valeur=validator.convert(valeur)
762           return valeur
763
764       def info_erreur_item(self):
765           chaine=""
766           a=1
767           for v in self.validators:
768               if v.info_erreur_item() != " " :
769                  if a==1:
770                     chaine=v.info_erreur_item()
771                     a=0
772                  else:
773                     chaine=chaine+" \n et "+v.info_erreur_item()
774           return chaine
775
776       def info_erreur_liste(self):
777           a=1
778           for v in self.validators:
779               if v.info_erreur_liste() != " " :
780                  if a==1:
781                     chaine=v.info_erreur_liste()
782                     a=0
783                  else:
784                     chaine=chaine+" \n et "+v.info_erreur_liste()
785           return chaine
786
787       def verif(self,valeur):
788           for validator in self.validators:
789               v=validator.verif(valeur)
790               if not v :
791                  self.local_info=validator.info()
792                  return 0
793           return 1
794
795       def verif_item(self,valeur):
796           for validator in self.validators:
797               v=validator.verif_item(valeur)
798               if not v :
799                  # L'info n'est probablement pas la meme que pour verif ???
800                  self.local_info=validator.info()
801                  return 0
802           return 1
803
804       def verif_cata(self):
805           infos=[]
806           for validator in self.validators:
807               v=validator.verif_cata()
808               if not v :infos.append(validator.cata_info)
809           if infos:
810              self.cata_info="\n".join(infos)
811              return 0
812           self.cata_info=""
813           return 1
814
815       def valide_liste_partielle(self,liste_courante=None):
816           """
817            Méthode de validation de liste partielle pour le validateur And.
818            Tous les validateurs gérés par le validateur And doivent considérer
819            la liste comme valide, pour que le validateur And la considère
820            comme valide.
821           """
822           for validator in self.validators:
823               v=validator.valide_liste_partielle(liste_courante)
824               if not v :
825                  return 0
826           return 1
827
828       def is_list(self):
829           """
830           Si plusieurs validateurs sont reliés par un ET
831           il faut que tous les validateurs attendent une liste
832           pour qu'on considère que leur intersection attende une liste.
833           Exemple Range(2,5) ET Card(1) n'attend pas une liste
834           Range(2,5) ET Pair attend une liste
835           """
836           for validator in self.validators:
837               v=validator.is_list()
838               if v == 0 :
839                  return 0
840           return 1
841
842       def has_into(self):
843           """
844           Dans le cas ou plusieurs validateurs sont reliés par un ET
845           il suffit qu'un seul validateur propose un choix pour
846           qu'on considère que leur intersection propose un choix.
847           Exemple : Enum(1,2,3) ET entier pair, propose un choix
848           En revanche, entier pair ET superieur à 10 ne propose pas de choix
849           """
850           for validator in self.validators:
851               v=validator.has_into()
852               if v :
853                  return 1
854           return 0
855
856       def get_into(self,liste_courante=None,into_courant=None):
857           """
858           Dans le cas ou plusieurs validateurs sont reliés par un ET
859           il suffit qu'un seul validateur propose un choix pour
860           qu'on considère que leur intersection propose un choix. Tous les
861           choix proposés par les validateurs sont croisés (opérateur
862           d'intersection)
863           Exemple : Enum(1,2,3) ET entier pair, propose un choix (2,)
864           En revanche, Enum(1,2,3) ET Enum(4,5,6) ne propose pas de choix.
865           """
866           for validator in self.validators:
867               into_courant=validator.get_into(liste_courante,into_courant)
868               if into_courant in ([],None):break
869           return into_courant
870
871 def do_liste(validators):
872     """ 
873        Convertit une arborescence de validateurs en OrVal ou AndVal
874        validators est une liste de validateurs ou de listes ou de tuples
875     """
876     valids=[]
877     for validator in validators:
878         if type(validator) == types.FunctionType:
879            valids.append(FunctionVal(validator))
880         elif type(validator) == types.TupleType:
881            valids.append(OrVal(do_liste(validator)))
882         elif type(validator) == types.ListType:
883            valids.append(AndVal(do_liste(validator)))
884         else:
885            valids.append(validator)
886     return valids
887
888 def validatorFactory(validator):
889     if type(validator) == types.FunctionType:
890        return FunctionVal(validator)
891     elif type(validator) == types.TupleType:
892        return OrVal(do_liste(validator))
893     elif type(validator) == types.ListType:
894        return AndVal(do_liste(validator))
895     else:
896        return validator
897
898 # Ci-dessous : exemples de validateur (peu testés)
899
900 class RangeVal(ListVal):
901       """
902           Exemple de classe validateur : verification qu'une valeur
903           est dans un intervalle.
904           Pour une liste on verifie que tous les elements sont 
905           dans l'intervalle
906           Susceptible de remplacer les attributs "vale_min" "vale_max"
907           dans les catalogues
908       """
909       def __init__(self,low,high):
910           self.low=low
911           self.high=high
912           self.cata_info="%s doit etre inferieur a %s" %(low,high)
913
914       def info(self):
915           return "valeur dans l'intervalle %s , %s" %(self.low,self.high)
916
917       def convert_item(self,valeur):
918           if valeur > self.low and valeur < self.high:return valeur
919           raise ValError("%s devrait etre comprise entre %s et %s" %(valeur,self.low,self.high))
920
921       def verif_item(self,valeur):
922           return valeur > self.low and valeur < self.high
923
924       def info_erreur_item(self) :
925           return "La valeur doit etre comprise entre %s et %s" % (self.low,
926                                                                   self.high)
927
928       def verif_cata(self):
929           if self.low > self.high : return 0
930           return 1
931
932 class CardVal(Valid):
933       """
934           Exemple de classe validateur : verification qu'une liste est
935           d'une longueur superieur a un minimum (min) et inferieure
936           a un maximum (max).
937           Susceptible de remplacer les attributs "min" "max" dans les
938           catalogues
939       """
940       def __init__(self,min='**',max='**'):
941           self.min=min
942           self.max=max  
943           self.cata_info="%s doit etre inferieur a %s" % (min,max)
944
945       def info(self):
946           return "longueur de liste comprise entre  %s et %s" % (self.min,self.max)
947
948       def info_erreur_liste(self):
949           return "Le cardinal de la liste doit etre compris entre %s et %s" % (self.min,self.max)
950
951       def is_list(self):
952           return self.max == '**' or self.max > 1
953
954       def get_into(self,liste_courante=None,into_courant=None):
955           if into_courant is None:
956              return None
957           elif liste_courante is None:
958              return into_courant
959           elif self.max == '**':
960              return into_courant
961           elif len(liste_courante) < self.max:
962              return into_courant
963           else:
964              return []
965
966       def convert(self,valeur):
967           if type(valeur) in (types.ListType,types.TupleType):
968              l=len(valeur)
969           elif valeur is None:
970              l=0
971           else:
972              l=1
973           if self.max != '**' and l > self.max:raise ValError("%s devrait etre de longueur inferieure a %s" %(valeur,self.max))
974           if self.min != '**' and l < self.min:raise ValError("%s devrait etre de longueur superieure a %s" %(valeur,self.min))
975           return valeur
976
977       def verif_item(self,valeur):
978           return 1
979
980       def verif(self,valeur):
981           if type(valeur) in (types.ListType,types.TupleType):
982              if self.max != '**' and len(valeur) > self.max:return 0
983              if self.min != '**' and len(valeur) < self.min:return 0
984              return 1
985           else:
986              if self.max != '**' and 1 > self.max:return 0
987              if self.min != '**' and 1 < self.min:return 0
988              return 1
989
990       def verif_cata(self):
991           if self.min != '**' and self.max != '**' and self.min > self.max : return 0
992           return 1
993
994       def valide_liste_partielle(self,liste_courante=None):
995           validite=1
996           if liste_courante != None :
997              if len(liste_courante) > self.max :
998                 validite=0
999           return validite
1000
1001 class PairVal(ListVal):
1002       """
1003           Exemple de classe validateur : verification qu'une valeur
1004           est paire.
1005           Pour une liste on verifie que tous les elements sont
1006           pairs
1007       """
1008       def __init__(self):
1009           ListVal.__init__(self)
1010           self.cata_info=""
1011
1012       def info(self):
1013           return "valeur paire"
1014
1015       def info_erreur_item(self):
1016           return "La valeur saisie doit etre paire"
1017
1018       def convert(self,valeur):
1019           for val in valeur:
1020              v=self.adapt(val)
1021              if v % 2 != 0:raise ValError("%s contient des valeurs non paires" % repr(valeur))
1022           return valeur
1023
1024       def default(self,valeur):
1025           return valeur
1026
1027       def verif_item(self,valeur):
1028           if type(valeur) == types.InstanceType:
1029              return 0
1030           return valeur % 2 == 0
1031
1032       def verif(self,valeur):
1033           if type(valeur) in (types.ListType,types.TupleType):
1034              for val in valeur:
1035                 if val % 2 != 0:return 0
1036              return 1
1037           else:
1038              if valeur % 2 != 0:return 0
1039              return 1
1040
1041 class EnumVal(ListVal):
1042       """
1043           Exemple de classe validateur : verification qu'une valeur
1044           est prise dans une liste de valeurs.
1045           Susceptible de remplacer l attribut "into" dans les catalogues
1046       """
1047       def __init__(self,into=()):
1048           if type(into) not in (types.ListType,types.TupleType): into=(into,)
1049           self.into=into
1050           self.cata_info=""
1051
1052       def info(self):
1053           return "valeur dans %s" % `self.into`
1054
1055       def convert_item(self,valeur):
1056           if valeur in self.into:return valeur
1057           raise ValError("%s contient des valeurs hors des choix possibles: %s " %(valeur,self.into))
1058
1059       def verif_item(self,valeur):
1060           if valeur not in self.into:return 0
1061           return 1
1062
1063       def has_into(self):
1064           return 1
1065
1066       def get_into(self,liste_courante=None,into_courant=None):
1067           if into_courant is None:
1068              liste_choix= list(self.into)
1069           else:
1070              liste_choix=[]
1071              for e in into_courant:
1072                  if e in self.into:
1073                     liste_choix.append(e)
1074           return liste_choix
1075
1076       def info_erreur_item(self):
1077           return "La valeur n'est pas dans la liste des choix possibles"
1078
1079 def ImpairVal(valeur):
1080     """
1081           Exemple de validateur
1082         Cette fonction est un validateur. Elle verifie que la valeur passee
1083         est bien un nombre impair.
1084     """
1085     if type(valeur) in (types.ListType,types.TupleType):
1086        for val in valeur:
1087            if val % 2 != 1:return 0
1088        return 1
1089     else:
1090        if valeur % 2 != 1:return 0
1091        return 1
1092
1093 ImpairVal.info="valeur impaire"
1094     
1095 class F1Val(Valid):
1096       """
1097           Exemple de validateur
1098           Cette classe est un validateur de dictionnaire (mot cle facteur ?). Elle verifie
1099           que la somme des cles A et B vaut une valeur donnee
1100           en parametre du validateur
1101       """
1102       def __init__(self,somme=10):
1103           self.somme=somme
1104           self.cata_info=""
1105
1106       def info(self):
1107           return "valeur %s pour la somme des cles A et B " % self.somme
1108
1109       def verif(self,valeur):
1110           if type(valeur) in (types.ListType,types.TupleType):
1111              for val in valeur:
1112                 if not val.has_key("A"):return 0
1113                 if not val.has_key("B"):return 0
1114                 if val["A"]+val["B"]  != self.somme:return 0
1115              return 1
1116           else:
1117              if not valeur.has_key("A"):return 0
1118              if not valeur.has_key("B"):return 0
1119              if valeur["A"]+valeur["B"]  != self.somme:return 0
1120              return 1
1121
1122 class FunctionVal(Valid):
1123       """
1124           Exemple de validateur
1125           Cette classe est un validateur qui est initialise avec une fonction
1126       """
1127       def __init__(self,function):
1128           self.function=function
1129
1130       def info(self):
1131           return self.function.info
1132
1133       def verif(self,valeur):
1134           return self.function(valeur)
1135
1136 CoercableFuncs = { types.IntType:     int,
1137                    types.LongType:    long,
1138                    types.FloatType:   float,
1139                    types.ComplexType: complex,
1140                    types.UnicodeType: unicode }
1141
1142 class TypeVal(ListVal):
1143       """
1144           Exemple de validateur
1145           Cette classe est un validateur qui controle qu'une valeur
1146           est bien du type Python attendu.
1147           Pour une liste on verifie que tous les elements sont du bon type.
1148       """
1149       def __init__(self, aType):
1150           if type(aType) != types.TypeType:
1151              aType=type(aType)
1152           self.aType=aType
1153           try:
1154              self.coerce=CoercableFuncs[ aType ]
1155           except:
1156              self.coerce = self.identity
1157
1158       def info(self):
1159           return "valeur de %s" % self.aType
1160
1161       def identity ( self, value ):
1162           if type( value ) == self.aType:
1163              return value
1164           raise ValError
1165
1166       def convert_item(self,valeur):
1167           return   self.coerce(valeur)
1168
1169       def verif_item(self,valeur):
1170           try:
1171              self.coerce(valeur)
1172           except:
1173              return 0
1174           return 1
1175
1176 class InstanceVal(ListVal):
1177       """
1178           Exemple de validateur
1179           Cette classe est un validateur qui controle qu'une valeur est
1180           bien une instance (au sens Python) d'une classe
1181           Pour une liste on verifie chaque element de la liste
1182       """
1183       def __init__(self,aClass):
1184           if type(aClass) == types.InstanceType:
1185              aClass=aClass.__class__
1186           self.aClass=aClass
1187
1188       def info(self):
1189           return "valeur d'instance de %s" % self.aClass.__name__
1190
1191       def verif_item(self,valeur):
1192           if not isinstance(valeur,self.aClass): return 0
1193           return 1
1194