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