Salome HOME
PN changement P1[4]
[tools/eficas.git] / Ihm / I_MACRO_ETAPE.py
1 # -*- coding: utf-8 -*-
2 #            CONFIGURATION MANAGEMENT OF EDF VERSION
3 # ======================================================================
4 # COPYRIGHT (C) 1991 - 2002  EDF R&D                  WWW.CODE-ASTER.ORG
5 # THIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
6 # IT UNDER THE TERMS OF THE GNU GENERAL PUBLIC LICENSE AS PUBLISHED BY
7 # THE FREE SOFTWARE FOUNDATION; EITHER VERSION 2 OF THE LICENSE, OR
8 # (AT YOUR OPTION) ANY LATER VERSION.
9 #
10 # THIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT
11 # WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF
12 # MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. SEE THE GNU
13 # GENERAL PUBLIC LICENSE FOR MORE DETAILS.
14 #
15 # YOU SHOULD HAVE RECEIVED A COPY OF THE GNU GENERAL PUBLIC LICENSE
16 # ALONG WITH THIS PROGRAM; IF NOT, WRITE TO EDF R&D CODE_ASTER,
17 #    1 AVENUE DU GENERAL DE GAULLE, 92141 CLAMART CEDEX, FRANCE.
18 #
19 #
20 # ======================================================================
21 """
22 """
23 # Modules Python
24 import sys
25 import traceback,types,string
26
27 # Modules Eficas
28 import I_ETAPE
29 from Noyau.N_ASSD import ASSD
30
31 # import rajoutés suite à l'ajout de Build_sd --> à résorber
32 import Noyau, Validation.V_MACRO_ETAPE
33 from Noyau import N_Exception
34 from Noyau.N_Exception import AsException
35 # fin import à résorber
36
37 class MACRO_ETAPE(I_ETAPE.ETAPE):
38
39   def __init__(self):
40       self.typret=None
41       self.recorded_units={}
42
43   def get_sdprods(self,nom_sd):
44     """ 
45          Fonction : retourne le concept produit par l etape de nom nom_sd
46          s il existe sinon None
47     """
48     if self.sd and self.sd.nom == nom_sd :return self.sd
49     for co in self.sdprods:
50       if co.nom == nom_sd:return co
51     if type(self.definition.op_init) == types.FunctionType:
52       d={}
53       apply(self.definition.op_init,(self,d))
54       return d.get(nom_sd,None)
55     return None
56
57   def get_contexte_jdc(self,fichier,text):
58     """ 
59          Interprète text comme un texte de jdc et retourne le 
60          contexte final
61          cad le dictionnaire des sd disponibles à la dernière étape
62          Si text n'est pas un texte de jdc valide, retourne None
63          ou leve une exception
64          --> utilisée par ops.POURSUITE et INCLUDE
65     """
66     try:
67        # on essaie de créer un objet JDC auxiliaire avec un contexte initial
68        context_ini = self.parent.get_contexte_avant(self)
69
70        # Indispensable avant de creer un nouveau JDC
71        CONTEXT.unset_current_step()
72        args=self.jdc.args
73        prefix_include=None
74        if hasattr(self,'prefix'):
75           prefix_include=self.prefix
76        # ATTENTION : le dictionnaire recorded_units sert à memoriser les unites des 
77        # fichiers inclus. Il est preferable de garder le meme dictionnaire pendant
78        # tout le traitement et de ne pas le reinitialiser brutalement (utiliser 
79        # clear plutot) si on ne veut pas perdre la memoire des unites.
80        # En principe si la memorisation est faite au bon moment il n'est pas necessaire
81        # de prendre cette precaution mais ce n'est pas vrai partout.
82        old_recorded_units=self.recorded_units.copy()
83        self.recorded_units.clear()
84
85        j=self.JdC_aux( procedure=text,cata=self.jdc.cata,
86                                 nom=fichier,
87                                 context_ini = context_ini,
88                                 appli=self.jdc.appli,
89                                 jdc_pere=self.jdc,etape_include=self,
90                                 prefix_include=prefix_include,
91                                 recorded_units=self.recorded_units,
92                                 old_recorded_units=old_recorded_units,**args)
93
94        j.analyse()
95        # On récupère les étapes internes (pour validation)
96        self.etapes=j.etapes
97     except:
98        traceback.print_exc()
99        # On force le contexte (etape courante) à self
100        CONTEXT.unset_current_step()
101        CONTEXT.set_current_step(self)
102        return None
103
104     if not j.cr.estvide():
105        # Erreurs dans l'INCLUDE. On garde la memoire du fichier 
106        # mais on n'insere pas les concepts
107        # On force le contexte (etape courante) à self
108        CONTEXT.unset_current_step()
109        CONTEXT.set_current_step(self)
110        raise Exception("Impossible de relire le fichier\n"+str(j.cr))
111
112     cr=j.report()
113     if not cr.estvide():
114        # L'INCLUDE n'est pas valide.
115        # On force le contexte (etape courante) à self
116        CONTEXT.unset_current_step()
117        CONTEXT.set_current_step(self)
118        raise Exception("Le fichier include contient des erreurs\n"+str(j.cr))
119
120     # On recupere le contexte de l'include verifie
121     try:
122        j_context=j.get_verif_contexte()
123     except:
124        CONTEXT.unset_current_step()
125        CONTEXT.set_current_step(self)
126        raise
127
128     # On remplit le dictionnaire des concepts produits inclus
129     # en retirant les concepts présents dans le  contexte initial
130     # On ajoute egalement le concept produit dans le sds_dict du parent
131     # sans verification car on est sur (verification integrée) que 
132     # le nommage est possible
133     self.g_context.clear()
134     for k,v in j_context.items():
135        if not context_ini.has_key(k) or context_ini[k] != v:
136            self.g_context[k]=v
137            self.parent.sds_dict[k]=v
138
139
140     # On recupere le contexte courant
141     self.current_context=j.current_context
142     self.index_etape_courante=j.index_etape_courante
143
144     # XXX j.supprime() ???
145     # On rétablit le contexte (etape courante) à self
146     CONTEXT.unset_current_step()
147     CONTEXT.set_current_step(self)
148
149     return j_context
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 
167          de la meme classe 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   def make_contexte_include(self,fichier,text):
251     """
252         Cette méthode sert à créer un contexte en interprétant un texte source
253         Python
254     """
255     # on récupère le contexte d'un nouveau jdc dans lequel on interprete text
256     contexte = self.get_contexte_jdc(fichier,text)
257     if contexte == None :
258       raise Exception("Impossible de construire le jeu de commandes correspondant au fichier")
259     else:
260       # Pour les macros de type include : INCLUDE, INCLUDE_MATERIAU et POURSUITE
261       # l'attribut g_context est un dictionnaire qui contient les concepts produits par inclusion
262       # l'attribut contexte_fichier_init est un dictionnaire qui contient les concepts produits
263       # en sortie de macro. g_context est obtenu en retirant de contexte_fichier_init les concepts
264       # existants en debut de macro contenus dans context_ini (dans get_contexte_jdc)
265       # g_context est utilisé pour avoir les concepts produits par la macro
266       # contexte_fichier_init est utilisé pour avoir les concepts supprimés par la macro
267       self.contexte_fichier_init = contexte
268
269   def reevalue_fichier_init(self):
270       """Recalcule les concepts produits par le fichier enregistre"""
271       old_context=self.contexte_fichier_init
272       try:
273          self.make_contexte_include(self.fichier_ini ,self.fichier_text)
274       except:
275          l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
276          self.fichier_err = string.join(l)
277          #self.etapes=[]
278          self.g_context={}
279
280          self.old_contexte_fichier_init=old_context
281          self.contexte_fichier_init={}
282          self.reevalue_sd_jdc()
283          return
284
285       # L'evaluation s'est bien passee
286       self.fichier_err = None
287       self.old_contexte_fichier_init=old_context
288       self.reevalue_sd_jdc()
289
290   def update_fichier_init(self,unite):
291       """Reevalue le fichier init sans demander (dans la mesure du possible) a l'utilisateur 
292          les noms des fichiers
293          Ceci suppose que les relations entre unites et noms ont été memorisees préalablement
294       """
295       
296       self.fichier_err=None
297       self.old_contexte_fichier_init=self.contexte_fichier_init
298
299       if unite != self.fichier_unite or not self.parent.recorded_units.has_key(unite):
300          # Changement d'unite ou Nouvelle unite
301          f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
302          units={}
303          if f is not None:
304             self.fichier_ini = f
305             self.fichier_text=text
306          self.recorded_units=units
307          if self.fichier_ini is None and self.jdc.appli:
308             self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier inclus",
309                      message="Ce fichier ne sera pas pris en compte\n"+"Le fichier associé n'est pas défini")
310       else:
311          # Meme unite existante
312          f,text,units=self.parent.recorded_units[unite]
313          self.fichier_ini = f
314          self.fichier_text=text
315          self.recorded_units=units
316
317       if self.fichier_ini is None:
318          # Le fichier n'est pas défini
319          self.fichier_err="Le fichier associé n'est pas défini"
320          self.parent.change_unit(unite,self,self.fichier_unite)
321          self.g_context={}
322          self.contexte_fichier_init={}
323          self.parent.reset_context()
324          self.reevalue_sd_jdc()
325          return
326
327       try:
328         self.make_contexte_include(self.fichier_ini,self.fichier_text)
329         # Les 3 attributs fichier_ini fichier_text recorded_units doivent etre corrects
330         # avant d'appeler change_unit
331         self.parent.change_unit(unite,self,self.fichier_unite)
332       except:
333         # Erreurs lors de l'evaluation de text dans un JDC auxiliaire
334         l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
335         # On conserve la memoire du nouveau fichier
336         # mais on n'utilise pas les concepts crees par ce fichier
337         # on met l'etape en erreur : fichier_err=string.join(l)
338         self.fichier_err=string.join(l)
339         self.parent.change_unit(unite,self,self.fichier_unite)
340         self.g_context={}
341         self.contexte_fichier_init={}
342
343       # Le contexte du parent doit etre reinitialise car les concepts 
344       # produits ont changé
345       self.parent.reset_context()
346       # Si des concepts ont disparu lors du changement de fichier, on 
347       # demande leur suppression
348       self.reevalue_sd_jdc()
349
350   def record_unite(self):
351       if self.nom == "POURSUITE":
352          self.parent.record_unit(None,self)
353       else:
354          if hasattr(self,'fichier_unite') : 
355             self.parent.record_unit(self.fichier_unite,self)
356
357   def get_file_memo(self,unite=None,fic_origine=''):
358       """Retourne le nom du fichier et le source correspondant a l'unite unite
359          Initialise en plus recorded_units
360       """
361       units={}
362       if self.parent.old_recorded_units.has_key(unite):
363          f,text,units=self.parent.old_recorded_units[unite]
364          self.recorded_units=units
365          return f,text
366       elif self.jdc :
367          f,text=self.jdc.get_file(unite=unite,fic_origine=fic_origine)
368       else:
369          f,text=None,None
370       self.recorded_units=units
371       if f is None and self.jdc.appli:
372          self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier inclus",
373                           message="Ce fichier ne sera pas pris en compte\n"+"Le fichier associé n'est pas défini")
374       return f,text
375
376 #ATTENTION SURCHARGE : cette methode surcharge celle de Noyau (a garder en synchro)
377   def get_file(self,unite=None,fic_origine=''):
378       """Retourne le nom du fichier et le source correspondant a l'unite unite
379          Initialise en plus recorded_units
380       """
381       units={}
382       if self.jdc :
383          f,text=self.jdc.get_file(unite=unite,fic_origine=fic_origine)
384       else:
385          f,text=None,None
386       self.recorded_units=units
387       return f,text
388
389 #ATTENTION SURCHARGE : cette methode surcharge celle de Noyau (a garder en synchro)
390   def make_include(self,unite=None):
391       """
392           Inclut un fichier dont l'unite logique est unite
393           Cette methode est appelee par la fonction sd_prod de la macro INCLUDE
394           Si l'INCLUDE est invalide, la methode doit produire une exception 
395           Sinon on retourne None. Les concepts produits par l'INCLUDE sont
396           pris en compte par le JDC parent lors du calcul du contexte (appel de ???)
397       """
398
399       # On supprime l'attribut unite qui bloque l'evaluation du source de l'INCLUDE
400       # car on ne s'appuie pas sur lui dans EFICAS mais sur l'attribut fichier_ini
401       del self.unite
402       # Si unite n'a pas de valeur, l'etape est forcement invalide. On peut retourner None
403       if not unite : return
404
405       if not hasattr(self,'fichier_ini') : 
406          # Si le fichier n'est pas defini on le demande
407          f,text=self.get_file_memo(unite=unite,fic_origine=self.parent.nom)
408          # On memorise le fichier retourne
409          self.fichier_ini  = f
410          self.fichier_text = text
411          self.contexte_fichier_init={}
412          self.fichier_unite=unite
413          self.fichier_err=None
414          import Extensions.jdc_include
415          self.JdC_aux=Extensions.jdc_include.JdC_include
416
417          if f is None:
418              self.fichier_err="Le fichier INCLUDE n est pas defini"
419              self.parent.record_unit(unite,self)
420              raise Exception(self.fichier_err)
421
422          try:
423            self.make_contexte_include(self.fichier_ini ,self.fichier_text)
424            self.parent.record_unit(unite,self)
425          except:
426            l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
427            if self.jdc.appli:
428               self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier inclus",
429                                             message="Ce fichier ne sera pas pris en compte\n"+string.join(l)
430                                            )
431            self.parent.record_unit(unite,self)
432            self.g_context={}
433            self.fichier_err = string.join(l)
434            self.contexte_fichier_init={}
435            raise
436
437       else:
438          # Si le fichier est deja defini on ne reevalue pas le fichier
439          # et on leve une exception si une erreur a été enregistrée
440          self.update_fichier_init(unite)
441          self.fichier_unite=unite
442          if self.fichier_err is not None: raise Exception(self.fichier_err)
443         
444
445 #ATTENTION SURCHARGE : cette methode surcharge celle de Noyau (a garder en synchro)
446   def make_contexte(self,fichier,text):
447     """
448         Cette méthode sert à créer un contexte pour INCLUDE_MATERIAU
449         en interprétant un texte source Python
450         Elle est appelee par la fonction sd_prd d'INCLUDE_MATERIAU
451     """
452     # On supprime l'attribut mat qui bloque l'evaluation du source de l'INCLUDE_MATERIAU
453     # car on ne s'appuie pas sur lui dans EFICAS mais sur l'attribut fichier_ini
454     if hasattr(self,'mat'):del self.mat
455     self.fichier_ini =fichier
456     self.fichier_unite =fichier
457     self.fichier_text=text
458     self.fichier_err=None 
459     self.contexte_fichier_init={}
460     # On specifie la classe a utiliser pour le JDC auxiliaire
461     import Extensions.jdc_include
462     self.JdC_aux=Extensions.jdc_include.JdC_include
463     try:
464        self.make_contexte_include(self.fichier_ini ,self.fichier_text)
465        self.parent.record_unit(self.fichier_unite,self)
466     except:
467        l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
468        self.fichier_err = string.join(l)
469        self.parent.record_unit(self.fichier_unite,self)
470        self.g_context={}
471        self.contexte_fichier_init={}
472        raise
473
474 #ATTENTION SURCHARGE : cette methode surcharge celle de Noyau (a garder en synchro)
475   def update_sdprod(self,cr='non'):
476      # Cette methode peut etre appelee dans EFICAS avec des mots cles de 
477      # la commande modifies. Ceci peut conduire a la construction ou
478      # a la reconstruction d'etapes dans le cas d'INCLUDE ou d'INCLUDE_MATERIAU
479      # Il faut donc positionner le current_step avant l'appel
480      CONTEXT.unset_current_step()
481      CONTEXT.set_current_step(self)
482      valid=Validation.V_MACRO_ETAPE.MACRO_ETAPE.update_sdprod(self,cr=cr)
483      CONTEXT.unset_current_step()
484      return valid
485
486 #ATTENTION SURCHARGE: cette methode surcharge celle de Noyau a garder en synchro 
487   def Build_sd(self,nom):
488       """
489            Methode de Noyau surchargee pour poursuivre malgre tout
490            si une erreur se produit pendant la creation du concept produit
491       """
492       try:
493          sd=Noyau.N_MACRO_ETAPE.MACRO_ETAPE.Build_sd(self,nom)
494          self.state="modified"
495       except AsException,e:
496          # Une erreur s'est produite lors de la construction du concept
497          # Comme on est dans EFICAS, on essaie de poursuivre quand meme
498          # Si on poursuit, on a le choix entre deux possibilités :
499          # 1. on annule la sd associée à self
500          # 2. on la conserve mais il faut la retourner
501          # On choisit de l'annuler
502          # En plus il faut rendre coherents sdnom et sd.nom
503          self.sd=None
504          self.sdnom=None
505          self.state="unchanged"
506          self.valid=0
507
508       return self.sd
509
510 #ATTENTION SURCHARGE: cette methode surcharge celle de Noyau a garder en synchro 
511   def make_poursuite(self):
512       """ Cette methode est appelée par la fonction sd_prod de la macro POURSUITE
513       """
514       if not hasattr(self,'fichier_ini') :
515          # Si le fichier n'est pas defini on le demande
516          f,text=self.get_file_memo(fic_origine=self.parent.nom)
517          # On memorise le fichier retourne
518          self.fichier_ini = f
519          self.fichier_unite = None
520          self.fichier_text = text
521          self.fichier_err=None
522          import Extensions.jdc_include
523          self.JdC_aux=Extensions.jdc_include.JdC_poursuite
524          self.contexte_fichier_init={}
525
526          if f is None:
527              self.fichier_err="Le fichier POURSUITE n'est pas defini"
528              self.parent.record_unit(None,self)
529              raise Exception(self.fichier_err)
530
531          try:
532            self.make_contexte_include(self.fichier_ini,self.fichier_text)
533            self.parent.record_unit(None,self)
534          except:
535            l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
536            if self.jdc.appli:
537               self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier poursuite",
538                                             message="Ce fichier ne sera pas pris en compte\n"+string.join(l)
539                                            )
540            self.parent.record_unit(None,self)
541            self.g_context={}
542            self.fichier_err = string.join(l)
543            self.contexte_fichier_init={}
544            raise
545
546       else:
547          # Si le fichier est deja defini on ne reevalue pas le fichier
548          # et on leve une exception si une erreur a été enregistrée
549          self.update_fichier_init(None)
550          if self.fichier_err is not None: raise Exception(self.fichier_err)
551