Salome HOME
5c00db692345551efc443def8dd0d5a5ae72711d
[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
41   def get_sdprods(self,nom_sd):
42     """ 
43          Fonction : retourne le concept produit par l etape de nom nom_sd
44                     s il existe sinon None
45     """
46     if self.sd and self.sd.nom == nom_sd :return self.sd
47     for co in self.sdprods:
48       if co.nom == nom_sd:return co
49     if type(self.definition.op_init) == types.FunctionType:
50       d={}
51       apply(self.definition.op_init,(self,d))
52       return d.get(nom_sd,None)
53     return None
54
55   def get_contexte_jdc(self,fichier,text):
56     """ 
57          Interprète text comme un texte de jdc et retourne le 
58          contexte final
59          cad le dictionnaire des sd disponibles à la dernière étape
60          Si text n'est pas un texte de jdc valide, retourne None
61          ou leve une exception
62          --> utilisée par ops.POURSUITE et INCLUDE
63     """
64     try:
65        # on essaie de créer un objet JDC auxiliaire avec un contexte initial
66        context_ini = self.parent.get_contexte_avant(self)
67
68        # Indispensable avant de creer un nouveau JDC
69        CONTEXT.unset_current_step()
70        args=self.jdc.args
71        prefix_include=None
72        if hasattr(self,'prefix'):
73           prefix_include=self.prefix
74
75        j=self.JdC_aux( procedure=text,cata=self.jdc.cata,
76                                 nom=fichier,
77                                 context_ini = context_ini,
78                                 appli=self.jdc.appli,
79                                 jdc_pere=self.jdc,etape_include=self,
80                                 prefix_include=prefix_include,**args)
81
82        j.analyse()
83     except:
84        traceback.print_exc()
85        # On force le contexte (etape courante) à self
86        CONTEXT.unset_current_step()
87        CONTEXT.set_current_step(self)
88        return None
89
90     if not j.cr.estvide():
91        # On force le contexte (etape courante) à self
92        CONTEXT.unset_current_step()
93        CONTEXT.set_current_step(self)
94        # Erreurs dans l'INCLUDE. On garde la memoire du fichier mais on n'insere pas les concepts
95        # et les etapes. Ce traitement doit etre fait par l'appelant qui recoit l'exception
96        raise Exception("Impossible de relire le fichier\n"+str(j.cr))
97
98     cr=j.report()
99     if not cr.estvide():
100        # On force le contexte (etape courante) à self
101        CONTEXT.unset_current_step()
102        CONTEXT.set_current_step(self)
103        raise Exception("Le fichier include contient des erreurs\n"+str(j.cr))
104
105     # Cette verification n'est plus necessaire elle est integree dans le JDC_INCLUDE
106     #self.verif_contexte(j_context)
107
108     # On recupere le contexte apres la derniere etape
109     j_context=j.get_contexte_avant(None)
110
111     # On remplit le dictionnaire des concepts produits inclus
112     # en retirant les concepts présents dans le  contexte initial
113     self.g_context.clear()
114     for k,v in j_context.items():
115        if not context_ini.has_key(k) or context_ini[k] != v:
116            self.g_context[k]=v
117
118     # On récupère les étapes internes (pour validation)
119     self.etapes=j.etapes
120
121     # ainsi que le contexte courant
122     self.current_context=j.current_context
123     self.index_etape_courante=j.index_etape_courante
124
125     # XXX j.supprime() ???
126     # On force le contexte (etape courante) à self
127     CONTEXT.unset_current_step()
128     CONTEXT.set_current_step(self)
129
130     return j_context
131
132   def verif_contexte(self,context):
133      """
134          On verifie que le contexte context peut etre inséré dans le jeu
135          de commandes à la position de self
136      """
137      for nom_sd,sd in context.items():
138         if not isinstance(sd,ASSD):continue
139         if self.parent.get_sd_apres_etape(nom_sd,etape=self):
140            # Il existe un concept apres self => impossible d'inserer
141            raise Exception("Impossible d'inclure le fichier. Un concept de nom " + 
142                            "%s existe déjà dans le jeu de commandes." % nom_sd)
143
144   def reevalue_sd_jdc(self):
145      """
146          Avec la liste des SD qui ont été supprimées, propage la 
147          disparition de ces SD dans toutes les étapes et descendants
148      """
149      l_sd_supp,l_sd_repl = self.diff_contextes()
150      for sd in l_sd_supp:
151         self.parent.delete_concept_after_etape(self,sd)
152      for old_sd,sd in l_sd_repl:
153         self.parent.replace_concept_after_etape(self,old_sd,sd)
154
155   def diff_contextes(self):
156      """ 
157          Réalise la différence entre les 2 contextes 
158          old_contexte_fichier_init et contexte_fichier_init
159          cad retourne la liste des sd qui ont disparu ou ne derivent pas de la meme classe
160          et des sd qui ont ete remplacees
161      """
162      if not hasattr(self,'old_contexte_fichier_init'):return [],[]
163      l_sd_suppressed = []
164      l_sd_replaced = []
165      for old_key in self.old_contexte_fichier_init.keys():
166        if not self.contexte_fichier_init.has_key(old_key):
167          if isinstance(self.old_contexte_fichier_init[old_key],ASSD):
168            l_sd_suppressed.append(self.old_contexte_fichier_init[old_key])
169        else:
170          if isinstance(self.old_contexte_fichier_init[old_key],ASSD):
171             # Un concept de meme nom existe
172             old_class=self.old_contexte_fichier_init[old_key].__class__
173             if not isinstance(self.contexte_fichier_init[old_key],old_class):
174                # S'il n'est pas d'une classe derivee, on le supprime
175                l_sd_suppressed.append(self.old_contexte_fichier_init[old_key])
176             else:
177                l_sd_replaced.append((self.old_contexte_fichier_init[old_key],self.contexte_fichier_init[old_key]))
178      return l_sd_suppressed,l_sd_replaced
179       
180   def control_sdprods(self,d):
181       """
182           Cette methode doit updater le contexte fournit par
183           l'appelant en argument (d) en fonction de sa definition
184           tout en verifiant que ses concepts produits ne sont pas
185           deja definis dans le contexte
186       """
187       if type(self.definition.op_init) == types.FunctionType:
188         apply(self.definition.op_init,(self,d))
189       if self.sd:
190         if d.has_key(self.sd.nom):
191            # Le concept est deja defini
192            if self.reuse and self.reuse is d[self.sd.nom]:
193               # Le concept est reutilise : situation normale
194               pass
195            else:
196               # Redefinition du concept, on l'annule
197               #XXX on pourrait simplement annuler son nom pour conserver les objets
198               # l'utilisateur n'aurait alors qu'a renommer le concept (faisable??)
199               self.sd=self.reuse=self.sdnom=None
200               self.init_modif()
201         else:
202            # Le concept n'est pas defini, on peut updater d
203            d[self.sd.nom]=self.sd
204       # On verifie les concepts a droite du signe =
205       for co in self.sdprods:
206         if d.has_key(co.nom) and co is not d[co.nom] :
207            self.delete_concept(co)
208         else:
209            d[co.nom]=co
210        
211
212   def supprime_sdprods(self):
213       """
214           Fonction:
215             Lors d'une destruction d'etape, detruit tous les concepts produits
216             Un opérateur n a qu un concept produit
217             Une procedure n'en a aucun
218             Une macro en a en général plus d'un
219       """
220       if not self.is_reentrant() :
221          # l'étape n'est pas réentrante
222          # le concept retourné par l'étape est à supprimer car il était
223          # créé par l'étape
224          if self.sd != None :
225             self.parent.del_sdprod(self.sd)
226             self.parent.delete_concept(self.sd)
227       # On détruit les concepts à droite du signe =
228       for co in self.sdprods:
229          self.parent.del_sdprod(co)
230          self.parent.delete_concept(co)
231       # Si la macro a des etapes et des concepts inclus, on les detruit
232       for nom_sd,co in self.g_context.items():
233          if not isinstance(co,ASSD):continue
234          self.parent.del_sdprod(co)
235          self.parent.delete_concept(co)
236       # On met g_context à blanc
237       self.g_context={}
238          
239 #ATTENTION : cette methode surcharge celle de Noyau (a garder en synchro)
240   def Build_sd(self,nom):
241      """
242         Construit le concept produit de l'opérateur. Deux cas 
243         peuvent se présenter :
244
245         - le parent n'est pas défini. Dans ce cas, l'étape prend en charge 
246           la création et le nommage du concept.
247
248         - le parent est défini. Dans ce cas, l'étape demande au parent la 
249           création et le nommage du concept.
250
251      """
252      if not self.isactif():return
253      # CCAR : meme modification que dans I_ETAPE
254      if not self.isvalid(sd='non') : return
255      self.sdnom=nom
256      try:
257         # On positionne la macro self en tant que current_step pour que les 
258         # étapes créées lors de l'appel à sd_prod et à op_init aient la macro
259         #  comme parent 
260         self.set_current_step()
261         if self.parent:
262            sd= self.parent.create_sdprod(self,nom)
263            if type(self.definition.op_init) == types.FunctionType: 
264               apply(self.definition.op_init,(self,self.parent.g_context))
265         else:
266            sd=self.get_sd_prod()
267            if sd != None and self.reuse == None:
268               # On ne nomme le concept que dans le cas de non reutilisation 
269               # d un concept
270               sd.nom=nom
271         self.reset_current_step()
272         if self.jdc and self.jdc.par_lot == "NON" :
273            self.Execute()
274         return sd
275      except AsException,e:
276         self.reset_current_step()
277         # Une erreur s'est produite lors de la construction du concept
278         # Comme on est dans EFICAS, on essaie de poursuivre quand meme
279         # Si on poursuit, on a le choix entre deux possibilités :
280         # 1. on annule la sd associée à self
281         # 2. on la conserve mais il faut qu'elle soit correcte et la retourner
282         # En plus il faut rendre coherents sdnom et sd.nom
283         # On choisit de retourner None et de mettre l'etape invalide 
284         self.sd=None
285         self.sdnom=None
286         self.state="unchanged"
287         self.valid=0
288         return self.sd
289         #raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
290         #                     'fichier : ',self.appel[1],e)
291      except EOFError:
292         raise
293      except :
294         self.reset_current_step()
295         l=traceback.format_exception(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
296         raise AsException("Etape ",self.nom,'ligne : ',self.appel[0],
297                           'fichier : ',self.appel[1]+'\n',
298                            string.join(l))
299
300   def make_contexte_include(self,fichier,text):
301     """
302         Cette méthode sert à créer un contexte en interprétant un texte source
303         Python
304     """
305     # on récupère le contexte d'un nouveau jdc dans lequel on interprete text
306     contexte = self.get_contexte_jdc(fichier,text)
307     if contexte == None :
308       raise Exception("Impossible de construire le jeu de commandes correspondant au fichier")
309     else:
310       # Pour les macros de type include : INCLUDE, INCLUDE_MATERIAU et POURSUITE
311       # l'attribut g_context est un dictionnaire qui contient les concepts produits par inclusion
312       # l'attribut contexte_fichier_init est un dictionnaire qui contient les concepts produits
313       # en sortie de macro. g_context est obtenu en retirant de contexte_fichier_init les concepts
314       # existants en debut de macro contenus dans context_ini (dans get_contexte_jdc)
315       # g_context est utilisé pour avoir les concepts produits par la macro
316       # contexte_fichier_init est utilisé pour avoir les concepts supprimés par la macro
317       self.contexte_fichier_init = contexte
318
319   def reevalue_fichier_init(self):
320       """Recalcule les concepts produits par le fichier enregistre"""
321       old_context=self.contexte_fichier_init
322       try:
323          self.make_contexte_include(self.fichier_ini ,self.fichier_text)
324       except:
325          l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
326          self.fichier_err = string.join(l)
327          self.etapes=[]
328          self.g_context={}
329
330          self.old_contexte_fichier_init=old_context
331          self.contexte_fichier_init={}
332          self.reevalue_sd_jdc()
333          return
334
335       # L'evaluation s'est bien passee
336       self.fichier_err = None
337       self.old_contexte_fichier_init=old_context
338       self.reevalue_sd_jdc()
339
340   def make_poursuite(self):
341       """ Cette methode est appelée par la fonction sd_prod de la macro POURSUITE
342       """
343       if not hasattr(self,'fichier_ini') :
344          # Si le fichier n'est pas defini on le demande
345          f,text=self.get_file(fic_origine=self.parent.nom)
346          # On memorise le fichier retourne
347          self.fichier_ini = f
348          self.fichier_text = text
349          import Extensions.jdc_include
350          self.JdC_aux=Extensions.jdc_include.JdC_poursuite
351          self.contexte_fichier_init={}
352          if f is None:
353              self.fichier_err="Le fichier POURSUITE n'est pas defini"
354          else:
355              self.fichier_err=None
356
357          if self.fichier_err is not None: raise Exception(self.fichier_err)
358
359          try:
360            self.make_contexte_include(self.fichier_ini,self.fichier_text)
361          except:
362            l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
363            if self.jdc.appli:
364               self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier poursuite",
365                                             message="Ce fichier ne sera pas pris en compte\n"+string.join(l)
366                                            )
367            self.g_context={}
368            self.etapes=[]
369            self.fichier_err = string.join(l)
370            self.contexte_fichier_init={}
371            raise
372
373       else:
374          # Si le fichier est deja defini on ne reevalue pas le fichier
375          # et on leve une exception si une erreur a été enregistrée
376          if self.fichier_err is not None: raise Exception(self.fichier_err)
377
378
379 #ATTENTION : cette methode surcharge celle de Noyau (a garder en synchro)
380   def make_include(self,unite=None):
381       """
382           Inclut un fichier dont l'unite logique est unite
383           Cette methode est appelee par la fonction sd_prod de la macro INCLUDE
384           Si l'INCLUDE est invalide, la methode doit produire une exception 
385           Sinon on retourne None. Les concepts produits par l'INCLUDE sont
386           pris en compte par le JDC parent lors du calcul du contexte (appel de ???)
387       """
388
389       # On supprime l'attribut unite qui bloque l'evaluation du source de l'INCLUDE
390       # car on ne s'appuie pas sur lui dans EFICAS mais sur l'attribut fichier_ini
391       del self.unite
392       # Si unite n'a pas de valeur, l'etape est forcement invalide. On peut retourner None
393       if not unite : return
394
395       if not hasattr(self,'fichier_ini') : 
396          # Si le fichier n'est pas defini on le demande
397          f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
398          # On memorise le fichier retourne
399          self.fichier_ini  = f
400          self.fichier_text = text
401          self.contexte_fichier_init={}
402          if f is None:
403              self.fichier_err="Le fichier INCLUDE n est pas defini"
404          else:
405              self.fichier_err=None
406          import Extensions.jdc_include
407          self.JdC_aux=Extensions.jdc_include.JdC_include
408
409          if self.fichier_err is not None: raise Exception(self.fichier_err)
410
411          try:
412            self.make_contexte_include(self.fichier_ini ,self.fichier_text)
413          except:
414            l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
415            if self.jdc.appli:
416               self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier inclus",
417                                             message="Ce fichier ne sera pas pris en compte\n"+string.join(l)
418                                            )
419            self.g_context={}
420            self.etapes=[]
421            self.fichier_err = string.join(l)
422            self.contexte_fichier_init={}
423            raise
424
425       else:
426          # Si le fichier est deja defini on ne reevalue pas le fichier
427          # et on leve une exception si une erreur a été enregistrée
428          #self.reevalue_fichier_init()
429          if self.fichier_err is not None: raise Exception(self.fichier_err)
430         
431
432 #ATTENTION : cette methode surcharge celle de Noyau (a garder en synchro)
433   def make_contexte(self,fichier,text):
434     """
435         Cette méthode sert à créer un contexte pour INCLUDE_MATERIAU
436         en interprétant un texte source Python
437         Elle est appelee par la fonction sd_prd d'INCLUDE_MATERIAU
438     """
439     # On supprime l'attribut mat qui bloque l'evaluation du source de l'INCLUDE_MATERIAU
440     # car on ne s'appuie pas sur lui dans EFICAS mais sur l'attribut fichier_ini
441     if hasattr(self,'mat'):del self.mat
442     self.fichier_ini =fichier
443     self.fichier_text=text
444     self.fichier_err=None 
445     self.contexte_fichier_init={}
446     # On specifie la classe a utiliser pour le JDC auxiliaire
447     import Extensions.jdc_include
448     self.JdC_aux=Extensions.jdc_include.JdC_include
449     try:
450        self.make_contexte_include(self.fichier_ini ,self.fichier_text)
451     except:
452        l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
453        self.g_context={}
454        self.etapes=[]
455        self.fichier_err = string.join(l)
456        self.contexte_fichier_init={}
457        raise
458
459 #ATTENTION : cette methode surcharge celle de Noyau (a garder en synchro)
460   def update_sdprod(self,cr='non'):
461      # Cette methode peut etre appelee dans EFICAS avec des mots cles de 
462      # la commande modifies. Ceci peut conduire a la construction ou
463      # a la reconstruction d'etapes dans le cas d'INCLUDE ou d'INCLUDE_MATERIAU
464      # Il faut donc positionner le current_step avant l'appel
465      CONTEXT.unset_current_step()
466      CONTEXT.set_current_step(self)
467      valid=Validation.V_MACRO_ETAPE.MACRO_ETAPE.update_sdprod(self,cr=cr)
468      CONTEXT.unset_current_step()
469      return valid
470