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