Salome HOME
CCAR: diverses corrections lies aux validateurs
[tools/eficas.git] / Ihm / I_VALIDATOR.py
1 """
2    Ce module contient des classes permettant de définir des validateurs
3    pour EFICAS. Ces classes constituent un complément à des classes existantes
4    dans Noyau/N_VALIDATOR.py ou de nouvelles classes de validation.
5    Ces classes complémentaires ne servent que pour l'IHM d'EFICAS.
6    Elles servent essentiellement à ajouter des comportements spécifiques
7    IHM aux classes existantes dans le Noyau.
8    Ces comportements pourront etre rapatries dans le Noyau quand leur
9    interface sera stabilisée.
10 """
11
12 import types
13 import Noyau.N_VALIDATOR
14
15 class Valid:
16    """
17         Cette classe est la classe mere de toutes les classes complémentaires
18         que l'on trouve dans Ihm.
19         Elle porte les comportements par défaut des méthodes des validateurs.
20    """
21
22    def info_erreur_item(self):
23        """
24           Cette méthode permet d'avoir un message d'erreur pour un item
25           dans une liste dans le cas ou le validateur fait des vérifications
26           sur les items d'une liste. Si le validateur fait des vérifications 
27           sur la liste elle meme et non sur ses items, la méthode
28           doit retourner une chaine vide.
29        """
30        return " "
31
32    def aide(self):
33        """
34           Cette methode retourne une chaine de caractère qui permet à EFICAS de construire
35           un message d'aide en ligne
36           En général, le message retourné est le meme que celui retourné par la 
37           méthode info
38        """
39        return self.info()
40
41    def info_erreur_liste(self):
42        """
43           Cette méthode a un comportement complémentaire de celui de info_erreur_item.
44           Elle retourne un message d'erreur lié uniquement aux vérifications sur la liste
45           elle meme et pas sur ses items. Dans le cas où le validateur ne fait pas de vérification
46           sur des listes, elle retourne une chaine vide
47        """
48        return " "
49
50    def is_list(self):
51        """
52           Cette méthode retourne un entier qui indique si le validateur permet les listes (valeur 1)
53           ou ne les permet pas (valeur 0).
54           Par défaut, un validateur n'autorise que des scalaires.
55        """
56        return 0
57
58    def has_into(self):
59        """
60           Cette méthode retourne un entier qui indique si le validateur propose une liste de choix (valeur 1)
61           ou n'en propose pas.
62           Par défaut, un validateur n'en propose pas.
63        """
64        return 0
65
66    def valide_liste_partielle(self,liste_courante):
67        """
68           Cette methode retourne un entier qui indique si liste_courante est partiellement valide (valeur 1)
69           ou invalide (valeur 0). La validation partielle concerne les listes en cours de construction : on
70           veut savoir si la liste en construction peut etre complétée ou si elle peut déjà etre considérée
71           comme invalide.
72           En général un validateur effectue la meme validation pour les listes partielles et les
73           listes complètes.
74        """
75        return self.verif(liste_courante)
76
77    def verif_item(self,valeur):
78        """
79           La methode verif du validateur effectue une validation complete de la valeur.
80           valeur peut etre un scalaire ou une liste. Le validateur doit traiter les 2
81           aspects s'il accepte des listes (dans ce cas la methode is_list doit retourner 1).
82           La methode valid_item sert pour effectuer des validations partielles de liste
83           Elle doit uniquement verifier la validite d'un item de liste mais pas les caracteristiques
84           de la liste
85        """
86        return 0
87
88    def get_into(self,liste_courante=None,into_courant=None):
89        """
90           Cette méthode retourne la liste de choix proposée par le validateur. Si le validateur ne propose
91           pas de liste de choix, la méthode retourne None.
92           L'argument d'entrée liste_courante, s'il est différent de None, donne la liste des choix déjà
93           effectués par l'utilisateur. Dans ce cas, la méthode get_into doit calculer la liste des choix
94           en en tenant compte. Par exemple, si le validateur n'autorise pas les répétitions, la liste des
95           choix retournée ne doit pas contenir les choix déjà contenus dans liste_courante.
96           L'argument d'entrée into_courant, s'il est différent de None, donne la liste des choix proposés
97           par d'autres validateurs. Dans ce cas, la méthode get_into doit calculer la liste des choix à retourner
98           en se limitant à cette liste initiale. Par exemple, si into_courant vaut (1,2,3) et que le validateur
99           propose la liste de choix (3,4,5), la méthode ne doit retourner que (3,).
100
101           La méthode get_into peut retourner une liste vide [], ce qui veut dire qu'il n'y a pas (ou plus) de choix possible
102           Cette situation peut etre normale : l''utilisateur a utilisé tous les choix, ou résulter d'une incohérence 
103           des validateurs : choix parmi (1,2,3) ET choix parmi (4,5,6). Il est impossible de faire la différence entre
104           ces deux situations.
105        """
106        return into_courant
107
108    def is_eval(self,valeur):
109        """
110            Cette méthode indique si valeur est un objet de type EVAL ou autre
111            que l'on ne cherchera pas à evaluer et qui doit etre considere comme toujours valide
112            Si c'est un objet de ce type elle retourne la valeur 1 sinon la valeur 0
113        """
114        if type(valeur) == types.InstanceType :
115         if hasattr(valeur,'__class__'):
116           if valeur.__class__.__name__ in ('EVAL','entier','reel','chaine','complexe','liste','PARAMETRE_EVAL') :
117              return 1
118        return 0
119
120    def is_param(self,valeur):
121        """
122            Cette méthode indique si valeur est un objet de type PARAMETRE
123            dont on cherchera à evaluer la valeur (valeur.valeur)
124        """
125        if type(valeur) == types.InstanceType :
126           if valeur.__class__.__name__ in ('PARAMETRE',):
127              return 1
128        return 0
129
130    def is_unknown(self,valeur):
131        """
132            Cette méthode indique si valeur est un objet de type inconnu
133            c'est à dire ni de type EVAL ni de type PARAMETRE
134        """
135        if type(valeur) == types.InstanceType :
136           if not self.is_eval(valeur) and not self.is_param(valeur):
137              return 1
138        return 0
139
140    def surcharge_verif(self,methode_verif_initiale,valeur):
141        if type(valeur) == types.InstanceType :
142           #CCAR: pour le moment on fait comme dans is_entier de V_MCSIMP.py
143           # mais il serait préférable d'appeler une méthode de valeur : valeur.AsType()
144           # qui donnerait le type générique de l'objet.
145           # Pour un objet de "type" entier on obtiendrait par exemple 'I'
146           if valeur.__class__.__name__ in ('EVAL','entier','reel','chaine','complexe','liste','PARAMETRE_EVAL') :
147              # On ne vérifie pas le type d'un EVAL ou d'un objet de classe entier, .... C'est toujours valide
148              return 1
149           elif valeur.__class__.__name__ in ('PARAMETRE',):
150              # Dans le cas d'un parametre, il faut tester si la valeur du parametre est un entier
151              valeur=valeur.valeur
152           else:
153              # Objet inconnu : invalide
154              print "Objet non reconnu dans surcharge_verif : %s" %`valeur`
155              return 0
156
157        return methode_verif_initiale(self,valeur)
158
159 class FunctionVal(Valid):pass
160
161 class OrVal(Valid):
162    def verif_item(self,valeur):
163        for validator in self.validators:
164            v=validator.verif_item(valeur)
165            if v :
166               return 1
167        return 0
168
169    def info_erreur_item(self):
170        l=[]
171        for v in self.validators:
172            err=v.info_erreur_item()
173            if err != " " : l.append(err)
174        chaine=" \n ou ".join(l)
175        return chaine
176
177    def info_erreur_liste(self):
178        l=[]
179        for v in self.validators:
180            err=v.info_erreur_liste()
181            if err != " " : l.append(err)
182        chaine=" \n ou ".join(l)
183        return chaine
184
185    def is_list(self):
186        """
187           Si plusieurs validateurs sont reliés par un OU
188           il suffit qu'un seul des validateurs attende une liste
189           pour qu'on considère que leur union attende une liste.
190        """
191        for validator in self.validators:
192            v=validator.is_list()
193            if v :
194               return 1
195        return 0
196
197    def has_into(self):
198        """
199           Dans le cas ou plusieurs validateurs sont reliés par un OU
200           il faut que tous les validateurs proposent un choix pour 
201           qu'on considère que leur union propose un choix.
202           Exemple : Enum(1,2,3) OU entier pair, ne propose pas de choix
203           En revanche, Enum(1,2,3) OU Enum(4,5,6) propose un choix (1,2,3,4,5,6)
204        """
205        for validator in self.validators:
206            v=validator.has_into()
207            if not v :
208               return 0
209        return 1
210
211    def get_into(self,liste_courante=None,into_courant=None):
212        """
213           Dans le cas ou plusieurs validateurs sont reliés par un OU
214           tous les validateurs doivent proposer un choix pour 
215           qu'on considère que leur union propose un choix.
216           Tous les choix proposés par les validateurs sont réunis (opérateur d'union)
217           Exemple : Enum(1,2,3) OU entier pair, ne propose pas de choix
218           En revanche, Enum(1,2,3) OU Enum(4,5,6) propose un choix (1,2,3,4,5,6)
219        """
220        validator_into=[]
221        for validator in self.validators:
222            v_into=validator.get_into(liste_courante,into_courant)
223            if v_into is None:
224               return v_into
225            validator_into.extend(v_into)
226        return validator_into
227     
228    def valide_liste_partielle(self,liste_courante=None):
229        """
230            Méthode de validation de liste partielle pour le validateur Or.
231            Si un des validateurs gérés par le validateur Or considère la liste comme 
232            valide, le validateur Or la considère comme valide.
233        """
234        for validator in self.validators:
235            v=validator.valide_liste_partielle(liste_courante)
236            if v :
237               return 1
238        return 0
239
240 class AndVal(Valid):
241    def info(self):
242        return "\n et ".join([v.info() for v in self.validators])
243
244    def info_erreur_item(self):
245        chaine=""
246        a=1
247        for v in self.validators:
248            if v.info_erreur_item() != " " :
249               if a==1:
250                  chaine=v.info_erreur_item()
251                  a=0
252               else:
253                  chaine=chaine+" \n et "+v.info_erreur_item()
254        return chaine
255
256    def info_erreur_liste(self):
257        a=1
258        for v in self.validators:
259            if v.info_erreur_liste() != " " :
260               if a==1:
261                  chaine=v.info_erreur_liste()
262                  a=0
263               else:
264                  chaine=chaine+" \n et "+v.info_erreur_liste()
265        return chaine
266
267    def verif_item(self,valeur):
268        for validator in self.validators:
269            v=validator.verif_item(valeur)
270            if not v :
271               # L'info n'est probablement pas la meme que pour verif ???
272               self.local_info=validator.info()
273               return 0
274        return 1
275
276    def valide_liste_partielle(self,liste_courante=None):
277        """
278            Méthode de validation de liste partielle pour le validateur And.
279            Tous les validateurs gérés par le validateur And doivent considèrer la liste comme 
280            valide, pour que le validateur And la considère comme valide.
281        """
282        for validator in self.validators:
283            v=validator.valide_liste_partielle(liste_courante)
284            if not v :
285               return 0
286        return 1
287
288    def is_list(self):
289        """
290           Si plusieurs validateurs sont reliés par un ET
291           il faut que tous les validateurs attendent une liste
292           pour qu'on considère que leur intersection attende une liste.
293           Exemple Range(2,5) ET Card(1) n'attend pas une liste
294           Range(2,5) ET Pair attend une liste
295        """
296        for validator in self.validators:
297            v=validator.is_list()
298            if v == 0 :
299               return 0
300        return 1
301
302    def has_into(self):
303        """
304           Dans le cas ou plusieurs validateurs sont reliés par un ET
305           il suffit qu'un seul validateur propose un choix pour 
306           qu'on considère que leur intersection propose un choix.
307           Exemple : Enum(1,2,3) ET entier pair, propose un choix
308           En revanche, entier pair ET superieur à 10 ne propose pas de choix 
309        """
310        for validator in self.validators:
311            v=validator.has_into()
312            if v :
313               return 1
314        return 0
315
316
317    def get_into(self,liste_courante=None,into_courant=None):
318        """
319           Dans le cas ou plusieurs validateurs sont reliés par un ET
320           il suffit qu'un seul validateur propose un choix pour 
321           qu'on considère que leur intersection propose un choix.
322
323           Tous les choix proposés par les validateurs sont croisés (opérateur d'intersection)
324           Exemple : Enum(1,2,3) ET entier pair, propose un choix (2,)
325           En revanche, Enum(1,2,3) ET Enum(4,5,6) ne propose pas de choix
326        """
327        for validator in self.validators:
328            into_courant=validator.get_into(liste_courante,into_courant)
329            if into_courant in ([],None):
330               return into_courant
331        return into_courant
332
333 class CardVal(Valid):
334    def info(self):
335        return "longueur de liste comprise entre  %s et %s" % (self.min,self.max)
336
337    def is_list(self):
338        if self.max == '**' or self.max > 1:
339              return 1
340        else:
341              return 0
342
343    def verif_item(self,valeur):
344        return 1
345
346    def valide_liste_partielle(self,liste_courante=None):
347         validite=1
348         if liste_courante != None :
349            if len(liste_courante) > self.max :
350               validite=0
351         return validite
352
353    def get_into(self,liste_courante=None,into_courant=None):
354        if into_courant is None:
355           return None
356        elif liste_courante is None:
357           return into_courant
358        elif self.max == '**':
359           return into_courant
360        elif len(liste_courante) < self.max:
361           return into_courant
362        else:
363           return []
364
365    def info_erreur_liste(self):
366        return "La cardinalité de la liste doit être comprise entre %s et %s" % (self.min,self.max)
367
368 class ListVal(Valid):
369    def is_list(self):
370        return 1
371
372    def get_into(self,liste_courante=None,into_courant=None):
373        """
374           Cette méthode get_into effectue un traitement général qui consiste
375           a filtrer la liste de choix into_courant, si elle existe, en ne conservant
376           que les valeurs valides (appel de la méthode valid)
377        """
378        if into_courant is None:
379           return None
380        else:
381           liste_choix=[]
382           for e in into_courant:
383               if self.verif(e):
384                  liste_choix.append(e)
385           return liste_choix
386
387 class EnumVal(ListVal):
388    def verif_item(self,valeur):
389        if valeur not in self.into:return 0
390        return 1
391
392    def has_into(self):
393        return 1
394  
395    def get_into(self,liste_courante=None,into_courant=None):
396        if into_courant is None:
397           liste_choix= list(self.into)
398        else:
399           liste_choix=[]
400           for e in into_courant:
401               if e in self.into:
402                  liste_choix.append(e)
403        return liste_choix
404
405    def info_erreur_item(self):
406        return "La valeur n'est pas dans la liste des choix possibles"
407           
408 class LongStr(ListVal):
409    def info_erreur_item(self):
410        return "Longueur de la chaine incorrecte"
411
412    def verif_item(self,valeur):
413        low=self.low
414        high=self.high
415        if valeur[0]=="'" and valeur[-1]=="'" :
416           low=low+2
417           high=high+2
418        if len(valeur) < low :return 0
419        if len(valeur) > high:return 0
420        return 1
421  
422 class RangeVal(ListVal):
423    def verif_item(self,valeur):
424        if valeur < self.low :return 0
425        if valeur > self.high:return 0
426        return 1
427
428    def info_erreur_item(self) :
429        return "La valeur doit être comprise entre %s et %s" % (self.low,self.high)
430
431 CoercableFuncs = { types.IntType:     int,
432                    types.LongType:    long,
433                    types.FloatType:   float,
434                    types.ComplexType: complex,
435                    types.UnicodeType: unicode }
436
437 class TypeVal(ListVal):
438       """
439           Cette classe est un validateur qui controle qu'une valeur
440           est bien du type Python attendu.
441           Pour une liste on verifie que tous les elements sont du bon type.
442       """
443       def __init__(self, aType):
444           if type(aType) != types.TypeType:
445              aType=type(aType)
446           self.aType=aType
447           try:
448              self.coerce=CoercableFuncs[ aType ]
449           except:
450              self.coerce = self.identity
451
452       def info(self):
453           return "valeur de %s" % self.aType
454
455       def identity ( self, value ):
456           if type( value ) == self.aType:
457              return value
458           raise ValError
459
460       def verif(self,valeur):
461           if type(valeur) in (types.ListType,types.TupleType):
462              for val in valeur:
463                  valid=self.verif_item(val)
464                  if valid == 0:return 0
465              return 1
466           else:
467              return self.verif_item(valeur)
468
469       def verif_item(self,valeur):
470           try:
471              self.coerce(valeur)
472           except:
473              return 0
474           return 1
475
476 class PairVal(ListVal):
477
478    def info_erreur_item(self):
479        return "La valeur saisie doit être paire"
480
481    #ATTENTION METHODE SURCHARGEE: a resorber dans une future version
482    def verif_item(self,valeur):
483        if self.is_eval(valeur):
484           return 1
485        elif self.is_param(valeur):
486           valeur=valeur.valeur
487        elif self.is_unknown(valeur):
488           return 0
489        return valeur % 2 == 0
490
491    def verif(self,valeur):
492           if self.is_param(valeur):
493              valeur=valeur.valeur
494           if type(valeur) in (types.ListType,types.TupleType):
495              for val in valeur:
496                 if not self.verif_item(val):
497                    return 0
498              return 1
499           else:
500              return self.verif_item(valeur)
501
502    def verif_old(self,valeur):
503        print "Ihm.I_MCSIMP.PairVal.verif: ",valeur
504        return self.surcharge_verif(Noyau.N_VALIDATOR.PairVal.verif,valeur)
505
506 class InstanceVal(ListVal):
507    def verif_item(self,valeur):
508        if not isinstance(valeur,self.aClass): return 0
509        return 1
510
511 class NoRepeat(ListVal):
512    def info(self):
513        return "pas de presence de doublon dans la liste"
514
515    def info_erreur_liste(self):
516        return "Les doublons ne sont pas permis"
517
518    def verif_item(self,valeur):
519        return 1
520
521    def get_into(self,liste_courante=None,into_courant=None):
522        """
523           Methode get_into spécifique pour validateur NoRepeat
524           on retourne une liste de choix qui ne contient aucune valeur de into_courant
525           déjà contenue dans liste_courante
526        """
527        if into_courant is None:
528           return None
529        else:
530           liste_choix=[]
531           for e in into_courant:
532               if e in liste_choix: continue
533               if liste_courante is not None and e in liste_courante: continue
534               liste_choix.append(e)
535           return liste_choix
536  
537 class OrdList(ListVal):
538    def verif_item(self,valeur):
539        return 1
540
541    def get_into(self,liste_courante=None,into_courant=None):
542        """
543           Methode get_into spécifique pour validateur OrdList 
544           on retourne une liste de choix qui ne contient aucune valeur de into_courant
545           dont la valeur est inférieure à la dernière valeur de liste_courante, si
546           elle est différente de None.
547        """
548        if into_courant is None:
549           return None
550        elif not liste_courante :
551           return into_courant
552        else:
553           liste_choix=[]
554           last_val=liste_choix[-1]
555           for e in into_courant:
556               if self.ord=='croissant' and e <= last_val:continue
557               if self.ord=='decroissant' and e >= last_val:continue
558               liste_choix.append(e)
559           return liste_choix
560
561    def info_erreur_liste(self) :
562        return "La liste doit être en ordre "+self.ord
563