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