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