Salome HOME
bb4a0e66a3f00318aefbe71b9b1bcc50ff2530b9
[tools/eficas.git] / Ihm / I_MACRO_ETAPE.py
1 #            CONFIGURATION MANAGEMENT OF EDF VERSION
2 # ======================================================================
3 # COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
4 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
5 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
6 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
7 # (AT YOUR OPTION) ANY LATER VERSION.
8 #
9 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
10 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
11 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
12 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
13 #
14 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
15 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
16 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
17 #
18 #
19 # ======================================================================
20 """
21 """
22 # Modules Python
23 import sys
24 import traceback,types,string
25
26 # Modules Eficas
27 import I_ETAPE
28 from Noyau.N_ASSD import ASSD
29
30 # import rajoutés suite à l'ajout de Build_sd --> à résorber
31 import Noyau, Validation.V_MACRO_ETAPE
32 from Noyau import N_Exception
33 from Noyau.N_Exception import AsException
34 # fin import à résorber
35
36 class MACRO_ETAPE(I_ETAPE.ETAPE):
37
38   def __init__(self):
39       self.typret=None
40       self.recorded_units={}
41
42   def get_sdprods(self,nom_sd):
43     """ 
44          Fonction : retourne le concept produit par l etape de nom nom_sd
45                     s il existe sinon None
46     """
47     if self.sd and self.sd.nom == nom_sd :return self.sd
48     for co in self.sdprods:
49       if co.nom == nom_sd:return co
50     if type(self.definition.op_init) == types.FunctionType:
51       d={}
52       apply(self.definition.op_init,(self,d))
53       return d.get(nom_sd,None)
54     return None
55
56   def get_contexte_jdc(self,fichier,text):
57     """ 
58          Interprète text comme un texte de jdc et retourne le 
59          contexte final
60          cad le dictionnaire des sd disponibles à la dernière étape
61          Si text n'est pas un texte de jdc valide, retourne None
62          ou leve une exception
63          --> utilisée par ops.POURSUITE et INCLUDE
64     """
65     try:
66        # on essaie de créer un objet JDC auxiliaire avec un contexte initial
67        context_ini = self.parent.get_contexte_avant(self)
68
69        # Indispensable avant de creer un nouveau JDC
70        CONTEXT.unset_current_step()
71        args=self.jdc.args
72        prefix_include=None
73        if hasattr(self,'prefix'):
74           prefix_include=self.prefix
75
76        j=self.JdC_aux( procedure=text,cata=self.jdc.cata,
77                                 nom=fichier,
78                                 context_ini = context_ini,
79                                 appli=self.jdc.appli,
80                                 jdc_pere=self.jdc,etape_include=self,
81                                 prefix_include=prefix_include,
82                                 recorded_units=self.recorded_units,**args)
83
84        j.analyse()
85        # On récupère les étapes internes (pour validation)
86        self.etapes=j.etapes
87     except:
88        traceback.print_exc()
89        # On force le contexte (etape courante) à self
90        CONTEXT.unset_current_step()
91        CONTEXT.set_current_step(self)
92        return None
93
94     if not j.cr.estvide():
95        # Erreurs dans l'INCLUDE. On garde la memoire du fichier mais on n'insere pas les concepts
96        # On force le contexte (etape courante) à self
97        CONTEXT.unset_current_step()
98        CONTEXT.set_current_step(self)
99        raise Exception("Impossible de relire le fichier\n"+str(j.cr))
100
101     cr=j.report()
102     if not cr.estvide():
103        # On force le contexte (etape courante) à self
104        CONTEXT.unset_current_step()
105        CONTEXT.set_current_step(self)
106        raise Exception("Le fichier include contient des erreurs\n"+str(j.cr))
107
108     # On recupere le contexte apres la derniere etape
109     j_context=j.get_contexte_avant(None)
110
111     # Cette verification n'est plus necessaire elle est integree dans le JDC_INCLUDE
112     self.verif_contexte(j_context)
113
114     # On remplit le dictionnaire des concepts produits inclus
115     # en retirant les concepts présents dans le  contexte initial
116     # On ajoute egalement le concept produit dans le sds_dict du parent
117     # sans verification car on est sur (verification integrée) que le nommage est possible
118     self.g_context.clear()
119     for k,v in j_context.items():
120        if not context_ini.has_key(k) or context_ini[k] != v:
121            self.g_context[k]=v
122            self.parent.sds_dict[k]=v
123
124
125     # On recupere le contexte courant
126     self.current_context=j.current_context
127     self.index_etape_courante=j.index_etape_courante
128
129     # XXX j.supprime() ???
130     # On rétablit le contexte (etape courante) à self
131     CONTEXT.unset_current_step()
132     CONTEXT.set_current_step(self)
133
134     return j_context
135
136   def verif_contexte(self,context):
137      """
138          On verifie que le contexte context peut etre inséré dans le jeu
139          de commandes à la position de self
140      """
141      for nom_sd,sd in context.items():
142         if not isinstance(sd,ASSD):continue
143         if self.parent.get_sd_apres_etape(nom_sd,etape=self):
144            # Il existe un concept apres self => impossible d'inserer
145            # On force le contexte (etape courante) à self
146            CONTEXT.unset_current_step()
147            CONTEXT.set_current_step(self)
148            raise Exception("Impossible d'inclure le fichier. Un concept de nom " + 
149                            "%s existe déjà dans le jeu de commandes." % nom_sd)
150
151   def reevalue_sd_jdc(self):
152      """
153          Avec la liste des SD qui ont été supprimées, propage la 
154          disparition de ces SD dans toutes les étapes et descendants
155      """
156      l_sd_supp,l_sd_repl = self.diff_contextes()
157      for sd in l_sd_supp:
158         self.parent.delete_concept_after_etape(self,sd)
159      for old_sd,sd in l_sd_repl:
160         self.parent.replace_concept_after_etape(self,old_sd,sd)
161
162   def diff_contextes(self):
163      """ 
164          Réalise la différence entre les 2 contextes 
165          old_contexte_fichier_init et contexte_fichier_init
166          cad retourne la liste des sd qui ont disparu ou ne derivent pas de la meme classe
167          et des sd qui ont ete remplacees
168      """
169      if not hasattr(self,'old_contexte_fichier_init'):return [],[]
170      l_sd_suppressed = []
171      l_sd_replaced = []
172      for old_key in self.old_contexte_fichier_init.keys():
173        if not self.contexte_fichier_init.has_key(old_key):
174          if isinstance(self.old_contexte_fichier_init[old_key],ASSD):
175            l_sd_suppressed.append(self.old_contexte_fichier_init[old_key])
176        else:
177          if isinstance(self.old_contexte_fichier_init[old_key],ASSD):
178             # Un concept de meme nom existe
179             old_class=self.old_contexte_fichier_init[old_key].__class__
180             if not isinstance(self.contexte_fichier_init[old_key],old_class):
181                # S'il n'est pas d'une classe derivee, on le supprime
182                l_sd_suppressed.append(self.old_contexte_fichier_init[old_key])
183             else:
184                l_sd_replaced.append((self.old_contexte_fichier_init[old_key],self.contexte_fichier_init[old_key]))
185      return l_sd_suppressed,l_sd_replaced
186       
187   def control_sdprods(self,d):
188       """
189           Cette methode doit updater le contexte fournit par
190           l'appelant en argument (d) en fonction de sa definition
191           tout en verifiant que ses concepts produits ne sont pas
192           deja definis dans le contexte
193       """
194       if hasattr(self,"fichier_unite"):
195          self.update_fichier_init(self.fichier_unite)
196          self.init_modif()
197
198       if type(self.definition.op_init) == types.FunctionType:
199         apply(self.definition.op_init,(self,d))
200       if self.sd:
201         if d.has_key(self.sd.nom):
202            # Le concept est deja defini
203            if self.reuse and self.reuse is d[self.sd.nom]:
204               # Le concept est reutilise : situation normale
205               pass
206            else:
207               # Redefinition du concept, on l'annule
208               #XXX on pourrait simplement annuler son nom pour conserver les objets
209               # l'utilisateur n'aurait alors qu'a renommer le concept (faisable??)
210               self.sd=self.reuse=self.sdnom=None
211               self.init_modif()
212         else:
213            # Le concept n'est pas defini, on peut updater d
214            d[self.sd.nom]=self.sd
215       # On verifie les concepts a droite du signe =
216       for co in self.sdprods:
217         if d.has_key(co.nom) and co is not d[co.nom] :
218            self.delete_concept(co)
219         else:
220            d[co.nom]=co
221        
222
223   def supprime_sdprods(self):
224       """
225           Fonction:
226             Lors d'une destruction d'etape, detruit tous les concepts produits
227             Un opérateur n a qu un concept produit
228             Une procedure n'en a aucun
229             Une macro en a en général plus d'un
230       """
231       if not self.is_reentrant() :
232          # l'étape n'est pas réentrante
233          # le concept retourné par l'étape est à supprimer car il était
234          # créé par l'étape
235          if self.sd != None :
236             self.parent.del_sdprod(self.sd)
237             self.parent.delete_concept(self.sd)
238       # On détruit les concepts à droite du signe =
239       for co in self.sdprods:
240          self.parent.del_sdprod(co)
241          self.parent.delete_concept(co)
242       # Si la macro a des etapes et des concepts inclus, on les detruit
243       for nom_sd,co in self.g_context.items():
244          if not isinstance(co,ASSD):continue
245          self.parent.del_sdprod(co)
246          self.parent.delete_concept(co)
247       # On met g_context à blanc
248       self.g_context={}
249          
250 #ATTENTION : cette methode surcharge celle de Noyau (a garder en synchro)
251   def Build_sd(self,nom):
252      """
253         Construit le concept produit de l'opérateur. Deux cas 
254         peuvent se présenter :
255
256         - le parent n'est pas défini. Dans ce cas, l'étape prend en charge 
257           la création et le nommage du concept.
258
259         - le parent est défini. Dans ce cas, l'étape demande au parent la 
260           création et le nommage du concept.
261
262      """
263      if not self.isactif():return
264      # CCAR : meme modification que dans I_ETAPE
265      if not self.isvalid(sd='non') : return
266      self.sdnom=nom
267      try:
268         # On positionne la macro self en tant que current_step pour que les 
269         # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
270         #  comme parent 
271         self.set_current_step()
272         if self.parent:
273            sd= self.parent.create_sdprod(self,nom)
274            if type(self.definition.op_init) == types.FunctionType: 
275               apply(self.definition.op_init,(self,self.parent.g_context))
276         else:
277            sd=self.get_sd_prod()
278            if sd != None and self.reuse == None:
279               # On ne nomme le concept que dans le cas de non reutilisation 
280               # d un concept
281               sd.nom=nom
282         self.reset_current_step()
283         # Si on est arrive ici, l'etape est valide
284         self.state="unchanged"
285         self.valid=1
286         if self.jdc and self.jdc.par_lot == "NON" :
287            self.Execute()
288         return sd
289      except AsException,e:
290         self.reset_current_step()
291         # Une erreur s'est produite lors de la construction du concept
292         # Comme on est dans EFICAS, on essaie de poursuivre quand meme
293         # Si on poursuit, on a le choix entre deux possibilités :
294         # 1. on annule la sd associée à self
295         # 2. on la conserve mais il faut qu'elle soit correcte et la retourner
296         # En plus il faut rendre coherents sdnom et sd.nom
297         # On choisit de retourner None et de mettre l'etape invalide 
298         self.sd=None
299         self.sdnom=None
300         self.state="unchanged"
301         self.valid=0
302         return self.sd
303         #raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
304         #                     'fichier : ',self.appel[1],e)
305      except EOFError:
306         raise
307      except :
308         self.reset_current_step()
309         l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
310         raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
311                           'fichier : ',self.appel[1]+'\n',
312                            string.join(l))
313
314   def make_contexte_include(self,fichier,text):
315     """
316         Cette méthode sert à créer un contexte en interprétant un texte source
317         Python
318     """
319     # on récupère le contexte d'un nouveau jdc dans lequel on interprete text
320     contexte = self.get_contexte_jdc(fichier,text)
321     if contexte == None :
322       raise Exception("Impossible de construire le jeu de commandes correspondant au fichier")
323     else:
324       # Pour les macros de type include : INCLUDE, INCLUDE_MATERIAU et POURSUITE
325       # l'attribut g_context est un dictionnaire qui contient les concepts produits par inclusion
326       # l'attribut contexte_fichier_init est un dictionnaire qui contient les concepts produits
327       # en sortie de macro. g_context est obtenu en retirant de contexte_fichier_init les concepts
328       # existants en debut de macro contenus dans context_ini (dans get_contexte_jdc)
329       # g_context est utilisé pour avoir les concepts produits par la macro
330       # contexte_fichier_init est utilisé pour avoir les concepts supprimés par la macro
331       self.contexte_fichier_init = contexte
332
333   def reevalue_fichier_init(self):
334       """Recalcule les concepts produits par le fichier enregistre"""
335       old_context=self.contexte_fichier_init
336       try:
337          self.make_contexte_include(self.fichier_ini ,self.fichier_text)
338       except:
339          l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
340          self.fichier_err = string.join(l)
341          #self.etapes=[]
342          self.g_context={}
343
344          self.old_contexte_fichier_init=old_context
345          self.contexte_fichier_init={}
346          self.reevalue_sd_jdc()
347          return
348
349       # L'evaluation s'est bien passee
350       self.fichier_err = None
351       self.old_contexte_fichier_init=old_context
352       self.reevalue_sd_jdc()
353
354   def update_fichier_init(self,unite):
355       """Reevalue le fichier init sans demander (dans la mesure du possible) a l'utilisateur 
356          les noms des fichiers
357          Ceci suppose que les relations entre unites et noms ont été memorisees préalablement
358       """
359       if unite != self.fichier_unite:
360          # Changement d'unite
361          f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
362          units={}
363          self.fichier_ini = f
364          self.fichier_text=text
365          self.recorded_units=units
366          # Les 3 attributs fichier_ini fichier_text recorded_units doivent etre corrects
367          # avant d'appeler change_unit
368          self.parent.change_unit(unite,self,self.fichier_unite)
369       elif not self.parent.recorded_units.has_key(unite):
370          # Nouvelle unite
371          f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
372          units={}
373          self.fichier_ini = f
374          self.fichier_text=text
375          self.recorded_units=units
376          self.parent.change_unit(unite,self,self.fichier_unite)
377       else:
378          # Meme unite existante
379          f,text,units=self.parent.recorded_units[unite]
380          self.fichier_ini = f
381          self.fichier_text=text
382          self.recorded_units=units
383
384       self.fichier_err=None
385       self.old_contexte_fichier_init=self.contexte_fichier_init
386
387       try:
388         self.make_contexte_include(f,text)
389       except:
390         # Erreurs lors de l'evaluation de text dans un JDC auxiliaire
391         l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
392         # On conserve la memoire du nouveau fichier
393         # mais on n'utilise pas les concepts crees par ce fichier
394         # on met l'etape en erreur : fichier_err=string.join(l)
395         self.fichier_err=string.join(l)
396         self.g_context={}
397         self.contexte_fichier_init={}
398
399       # Le contexte du parent doit etre reinitialise car les concepts produits ont changé
400       self.parent.reset_context()
401       # Si des concepts ont disparu lors du changement de fichier, on demande leur suppression
402       self.reevalue_sd_jdc()
403
404   def record_unite(self):
405       if self.nom == "POURSUITE":
406          self.parent.record_unit(None,self)
407       else:
408          if hasattr(self,'fichier_unite') : 
409             self.parent.record_unit(self.fichier_unite,self)
410
411   def make_poursuite(self):
412       """ Cette methode est appelée par la fonction sd_prod de la macro POURSUITE
413       """
414       if not hasattr(self,'fichier_ini') :
415          # Si le fichier n'est pas defini on le demande
416          f,text=self.get_file(fic_origine=self.parent.nom)
417          # On memorise le fichier retourne
418          self.fichier_ini = f
419          self.fichier_unite = None
420          self.fichier_text = text
421          import Extensions.jdc_include
422          self.JdC_aux=Extensions.jdc_include.JdC_poursuite
423          self.contexte_fichier_init={}
424          self.parent.record_unit(None,self)
425          if f is None:
426              self.fichier_err="Le fichier POURSUITE n'est pas defini"
427          else:
428              self.fichier_err=None
429
430          if self.fichier_err is not None: raise Exception(self.fichier_err)
431
432          try:
433            self.make_contexte_include(self.fichier_ini,self.fichier_text)
434          except:
435            l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
436            if self.jdc.appli:
437               self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier poursuite",
438                                             message="Ce fichier ne sera pas pris en compte\n"+string.join(l)
439                                            )
440            self.g_context={}
441            #self.etapes=[]
442            self.fichier_err = string.join(l)
443            self.contexte_fichier_init={}
444            raise
445
446       else:
447          # Si le fichier est deja defini on ne reevalue pas le fichier
448          # et on leve une exception si une erreur a été enregistrée
449          self.update_fichier_init(None)
450          if self.fichier_err is not None: raise Exception(self.fichier_err)
451
452   def get_file(self,unite=None,fic_origine=''):
453       """Retourne le nom du fichier et le source correspondant a l'unite unite
454          Initialise en plus recorded_units
455       """
456       units={}
457       if self.jdc :
458          f,text=self.jdc.get_file(unite=unite,fic_origine=fic_origine)
459       else:
460          f,text=None,None
461       self.recorded_units=units
462       return f,text
463
464 #ATTENTION : cette methode surcharge celle de Noyau (a garder en synchro)
465   def make_include(self,unite=None):
466       """
467           Inclut un fichier dont l'unite logique est unite
468           Cette methode est appelee par la fonction sd_prod de la macro INCLUDE
469           Si l'INCLUDE est invalide, la methode doit produire une exception 
470           Sinon on retourne None. Les concepts produits par l'INCLUDE sont
471           pris en compte par le JDC parent lors du calcul du contexte (appel de ???)
472       """
473
474       # On supprime l'attribut unite qui bloque l'evaluation du source de l'INCLUDE
475       # car on ne s'appuie pas sur lui dans EFICAS mais sur l'attribut fichier_ini
476       del self.unite
477       # Si unite n'a pas de valeur, l'etape est forcement invalide. On peut retourner None
478       if not unite : return
479
480       if not hasattr(self,'fichier_ini') : 
481          # Si le fichier n'est pas defini on le demande
482          f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
483          # On memorise le fichier retourne
484          self.fichier_ini  = f
485          self.fichier_text = text
486          self.contexte_fichier_init={}
487          self.fichier_unite=unite
488          self.parent.record_unit(unite,self)
489          if f is None:
490              self.fichier_err="Le fichier INCLUDE n est pas defini"
491          else:
492              self.fichier_err=None
493          import Extensions.jdc_include
494          self.JdC_aux=Extensions.jdc_include.JdC_include
495
496          if self.fichier_err is not None: raise Exception(self.fichier_err)
497
498          try:
499            self.make_contexte_include(self.fichier_ini ,self.fichier_text)
500          except:
501            l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
502            if self.jdc.appli:
503               self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier inclus",
504                                             message="Ce fichier ne sera pas pris en compte\n"+string.join(l)
505                                            )
506            self.g_context={}
507            #self.etapes=[]
508            self.fichier_err = string.join(l)
509            self.contexte_fichier_init={}
510            raise
511
512       else:
513          # Si le fichier est deja defini on ne reevalue pas le fichier
514          # et on leve une exception si une erreur a été enregistrée
515          self.update_fichier_init(unite)
516          self.fichier_unite=unite
517          if self.fichier_err is not None: raise Exception(self.fichier_err)
518         
519
520 #ATTENTION : cette methode surcharge celle de Noyau (a garder en synchro)
521   def make_contexte(self,fichier,text):
522     """
523         Cette méthode sert à créer un contexte pour INCLUDE_MATERIAU
524         en interprétant un texte source Python
525         Elle est appelee par la fonction sd_prd d'INCLUDE_MATERIAU
526     """
527     # On supprime l'attribut mat qui bloque l'evaluation du source de l'INCLUDE_MATERIAU
528     # car on ne s'appuie pas sur lui dans EFICAS mais sur l'attribut fichier_ini
529     if hasattr(self,'mat'):del self.mat
530     self.fichier_ini =fichier
531     self.fichier_unite =fichier
532     self.fichier_text=text
533     self.parent.record_unit(self.fichier_unite,self)
534     self.fichier_err=None 
535     self.contexte_fichier_init={}
536     # On specifie la classe a utiliser pour le JDC auxiliaire
537     import Extensions.jdc_include
538     self.JdC_aux=Extensions.jdc_include.JdC_include
539     try:
540        self.make_contexte_include(self.fichier_ini ,self.fichier_text)
541     except:
542        l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
543        self.g_context={}
544        #self.etapes=[]
545        self.fichier_err = string.join(l)
546        self.contexte_fichier_init={}
547        raise
548
549 #ATTENTION : cette methode surcharge celle de Noyau (a garder en synchro)
550   def update_sdprod(self,cr='non'):
551      # Cette methode peut etre appelee dans EFICAS avec des mots cles de 
552      # la commande modifies. Ceci peut conduire a la construction ou
553      # a la reconstruction d'etapes dans le cas d'INCLUDE ou d'INCLUDE_MATERIAU
554      # Il faut donc positionner le current_step avant l'appel
555      CONTEXT.unset_current_step()
556      CONTEXT.set_current_step(self)
557      valid=Validation.V_MACRO_ETAPE.MACRO_ETAPE.update_sdprod(self,cr=cr)
558      CONTEXT.unset_current_step()
559      return valid
560