Salome HOME
PN bug notation scientifique
[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     if not j.isvalid():
113        # L'INCLUDE n'est pas valide.
114        # on produit un rapport d'erreurs
115        # On force le contexte (etape courante) à self
116        cr=j.report()
117        CONTEXT.unset_current_step()
118        CONTEXT.set_current_step(self)
119        raise Exception("Le fichier include contient des erreurs\n"+str(cr))
120
121     # Si aucune erreur rencontrée
122     # On recupere le contexte de l'include verifie
123     try:
124        j_context=j.get_verif_contexte()
125     except:
126        CONTEXT.unset_current_step()
127        CONTEXT.set_current_step(self)
128        raise
129
130     # On remplit le dictionnaire des concepts produits inclus
131     # en retirant les concepts présents dans le  contexte initial
132     # On ajoute egalement le concept produit dans le sds_dict du parent
133     # sans verification car on est sur (verification integrée) que 
134     # le nommage est possible
135     self.g_context.clear()
136     for k,v in j_context.items():
137        if not context_ini.has_key(k) or context_ini[k] != v:
138            self.g_context[k]=v
139            self.parent.sds_dict[k]=v
140
141
142     # On recupere le contexte courant
143     self.current_context=j.current_context
144     self.index_etape_courante=j.index_etape_courante
145
146     # XXX j.supprime() ???
147     # On rétablit le contexte (etape courante) à self
148     CONTEXT.unset_current_step()
149     CONTEXT.set_current_step(self)
150
151     return j_context
152
153   def reevalue_sd_jdc(self):
154      """
155          Avec la liste des SD qui ont été supprimées, propage la 
156          disparition de ces SD dans toutes les étapes et descendants
157      """
158      l_sd_supp,l_sd_repl = self.diff_contextes()
159      for sd in l_sd_supp:
160         self.parent.delete_concept_after_etape(self,sd)
161      for old_sd,sd in l_sd_repl:
162         self.parent.replace_concept_after_etape(self,old_sd,sd)
163
164   def diff_contextes(self):
165      """ 
166          Réalise la différence entre les 2 contextes 
167          old_contexte_fichier_init et contexte_fichier_init
168          cad retourne la liste des sd qui ont disparu ou ne derivent pas 
169          de la meme classe et des sd qui ont ete remplacees
170      """
171      if not hasattr(self,'old_contexte_fichier_init'):return [],[]
172      l_sd_suppressed = []
173      l_sd_replaced = []
174      for old_key in self.old_contexte_fichier_init.keys():
175        if not self.contexte_fichier_init.has_key(old_key):
176          if isinstance(self.old_contexte_fichier_init[old_key],ASSD):
177            l_sd_suppressed.append(self.old_contexte_fichier_init[old_key])
178        else:
179          if isinstance(self.old_contexte_fichier_init[old_key],ASSD):
180             # Un concept de meme nom existe
181             old_class=self.old_contexte_fichier_init[old_key].__class__
182             if not isinstance(self.contexte_fichier_init[old_key],old_class):
183                # S'il n'est pas d'une classe derivee, on le supprime
184                l_sd_suppressed.append(self.old_contexte_fichier_init[old_key])
185             else:
186                l_sd_replaced.append((self.old_contexte_fichier_init[old_key],self.contexte_fichier_init[old_key]))
187      return l_sd_suppressed,l_sd_replaced
188       
189   def control_sdprods(self,d):
190       """
191           Cette methode doit updater le contexte fournit par
192           l'appelant en argument (d) en fonction de sa definition
193           tout en verifiant que ses concepts produits ne sont pas
194           deja definis dans le contexte
195       """
196       if hasattr(self,"fichier_unite"):
197          self.update_fichier_init(self.fichier_unite)
198          self.init_modif()
199
200       if type(self.definition.op_init) == types.FunctionType:
201         apply(self.definition.op_init,(self,d))
202       if self.sd:
203         if d.has_key(self.sd.nom):
204            # Le concept est deja defini
205            if self.reuse and self.reuse is d[self.sd.nom]:
206               # Le concept est reutilise : situation normale
207               pass
208            else:
209               # Redefinition du concept, on l'annule
210               #XXX on pourrait simplement annuler son nom pour conserver les objets
211               # l'utilisateur n'aurait alors qu'a renommer le concept (faisable??)
212               self.sd=self.reuse=self.sdnom=None
213               self.init_modif()
214         else:
215            # Le concept n'est pas defini, on peut updater d
216            d[self.sd.nom]=self.sd
217       # On verifie les concepts a droite du signe =
218       for co in self.sdprods:
219         if d.has_key(co.nom) and co is not d[co.nom] :
220            self.delete_concept(co)
221         else:
222            d[co.nom]=co
223        
224
225   def supprime_sdprods(self):
226       """
227           Fonction:
228           Lors d'une destruction d'etape, detruit tous les concepts produits
229           Un opérateur n a qu un concept produit
230           Une procedure n'en a aucun
231           Une macro en a en général plus d'un
232       """
233       if not self.is_reentrant() :
234          # l'étape n'est pas réentrante
235          # le concept retourné par l'étape est à supprimer car il était
236          # créé par l'étape
237          if self.sd != None :
238             self.parent.del_sdprod(self.sd)
239             self.parent.delete_concept(self.sd)
240       # On détruit les concepts à droite du signe =
241       for co in self.sdprods:
242          self.parent.del_sdprod(co)
243          self.parent.delete_concept(co)
244       # Si la macro a des etapes et des concepts inclus, on les detruit
245       for nom_sd,co in self.g_context.items():
246          if not isinstance(co,ASSD):continue
247          self.parent.del_sdprod(co)
248          self.parent.delete_concept(co)
249       # On met g_context à blanc
250       self.g_context={}
251          
252   def make_contexte_include(self,fichier,text):
253     """
254         Cette méthode sert à créer un contexte en interprétant un texte source
255         Python
256     """
257     # on récupère le contexte d'un nouveau jdc dans lequel on interprete text
258     contexte = self.get_contexte_jdc(fichier,text)
259     if contexte == None :
260       raise Exception("Impossible de construire le jeu de commandes correspondant au fichier")
261     else:
262       # Pour les macros de type include : INCLUDE, INCLUDE_MATERIAU et POURSUITE
263       # l'attribut g_context est un dictionnaire qui contient les concepts produits par inclusion
264       # l'attribut contexte_fichier_init est un dictionnaire qui contient les concepts produits
265       # en sortie de macro. g_context est obtenu en retirant de contexte_fichier_init les concepts
266       # existants en debut de macro contenus dans context_ini (dans get_contexte_jdc)
267       # g_context est utilisé pour avoir les concepts produits par la macro
268       # contexte_fichier_init est utilisé pour avoir les concepts supprimés par la macro
269       self.contexte_fichier_init = contexte
270
271   def reevalue_fichier_init(self):
272       """Recalcule les concepts produits par le fichier enregistre"""
273       old_context=self.contexte_fichier_init
274       try:
275          self.make_contexte_include(self.fichier_ini ,self.fichier_text)
276       except:
277          l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
278          self.fichier_err = string.join(l)
279          #self.etapes=[]
280          self.g_context={}
281
282          self.old_contexte_fichier_init=old_context
283          self.contexte_fichier_init={}
284          self.reevalue_sd_jdc()
285          return
286
287       # L'evaluation s'est bien passee
288       self.fichier_err = None
289       self.old_contexte_fichier_init=old_context
290       self.reevalue_sd_jdc()
291
292   def update_fichier_init(self,unite):
293       """Reevalue le fichier init sans demander (dans la mesure du possible) a l'utilisateur 
294          les noms des fichiers
295          Ceci suppose que les relations entre unites et noms ont été memorisees préalablement
296       """
297       
298       self.fichier_err=None
299       self.old_contexte_fichier_init=self.contexte_fichier_init
300
301       if unite != self.fichier_unite or not self.parent.recorded_units.has_key(unite):
302          # Changement d'unite ou Nouvelle unite
303          f,text=self.get_file(unite=unite,fic_origine=self.parent.nom)
304          units={}
305          if f is not None:
306             self.fichier_ini = f
307             self.fichier_text=text
308          self.recorded_units=units
309          if self.fichier_ini is None and self.jdc.appli:
310             self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier inclus",
311                      message="Ce fichier ne sera pas pris en compte\n"+"Le fichier associé n'est pas défini")
312       else:
313          # Meme unite existante
314          f,text,units=self.parent.recorded_units[unite]
315          self.fichier_ini = f
316          self.fichier_text=text
317          self.recorded_units=units
318
319       if self.fichier_ini is None:
320          # Le fichier n'est pas défini
321          self.fichier_err="Le fichier associé n'est pas défini"
322          self.parent.change_unit(unite,self,self.fichier_unite)
323          self.g_context={}
324          self.contexte_fichier_init={}
325          self.parent.reset_context()
326          self.reevalue_sd_jdc()
327          return
328
329       try:
330         self.make_contexte_include(self.fichier_ini,self.fichier_text)
331         # Les 3 attributs fichier_ini fichier_text recorded_units doivent etre corrects
332         # avant d'appeler change_unit
333         self.parent.change_unit(unite,self,self.fichier_unite)
334       except:
335         # Erreurs lors de l'evaluation de text dans un JDC auxiliaire
336         l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
337         # On conserve la memoire du nouveau fichier
338         # mais on n'utilise pas les concepts crees par ce fichier
339         # on met l'etape en erreur : fichier_err=string.join(l)
340         self.fichier_err=string.join(l)
341         self.parent.change_unit(unite,self,self.fichier_unite)
342         self.g_context={}
343         self.contexte_fichier_init={}
344
345       # Le contexte du parent doit etre reinitialise car les concepts 
346       # produits ont changé
347       self.parent.reset_context()
348       # Si des concepts ont disparu lors du changement de fichier, on 
349       # demande leur suppression
350       self.reevalue_sd_jdc()
351
352   def record_unite(self):
353       if self.nom == "POURSUITE":
354          self.parent.record_unit(None,self)
355       else:
356          if hasattr(self,'fichier_unite') : 
357             self.parent.record_unit(self.fichier_unite,self)
358
359   def get_file_memo(self,unite=None,fic_origine=''):
360       """Retourne le nom du fichier et le source correspondant a l'unite unite
361          Initialise en plus recorded_units
362       """
363       units={}
364       if self.parent.old_recorded_units.has_key(unite):
365          f,text,units=self.parent.old_recorded_units[unite]
366          self.recorded_units=units
367          return f,text
368       elif self.jdc :
369          f,text=self.jdc.get_file(unite=unite,fic_origine=fic_origine)
370       else:
371          f,text=None,None
372       self.recorded_units=units
373       if f is None and self.jdc.appli:
374          self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier inclus",
375                           message="Ce fichier ne sera pas pris en compte\n"+"Le fichier associé n'est pas défini")
376       return f,text
377
378 #ATTENTION SURCHARGE : cette methode surcharge celle de Noyau (a garder en synchro)
379   def get_file(self,unite=None,fic_origine=''):
380       """Retourne le nom du fichier et le source correspondant a l'unite unite
381          Initialise en plus recorded_units
382       """
383       units={}
384       if self.jdc :
385          f,text=self.jdc.get_file(unite=unite,fic_origine=fic_origine)
386       else:
387          f,text=None,None
388       self.recorded_units=units
389       return f,text
390
391 #ATTENTION SURCHARGE : cette methode surcharge celle de Noyau (a garder en synchro)
392   def make_include(self,unite=None):
393       """
394           Inclut un fichier dont l'unite logique est unite
395           Cette methode est appelee par la fonction sd_prod de la macro INCLUDE
396           Si l'INCLUDE est invalide, la methode doit produire une exception 
397           Sinon on retourne None. Les concepts produits par l'INCLUDE sont
398           pris en compte par le JDC parent lors du calcul du contexte (appel de ???)
399       """
400
401       # On supprime l'attribut unite qui bloque l'evaluation du source de l'INCLUDE
402       # car on ne s'appuie pas sur lui dans EFICAS mais sur l'attribut fichier_ini
403       del self.unite
404       # Si unite n'a pas de valeur, l'etape est forcement invalide. On peut retourner None
405       if not unite : return
406
407       if not hasattr(self,'fichier_ini') : 
408          # Si le fichier n'est pas defini on le demande
409          f,text=self.get_file_memo(unite=unite,fic_origine=self.parent.nom)
410          # On memorise le fichier retourne
411          self.fichier_ini  = f
412          self.fichier_text = text
413          self.contexte_fichier_init={}
414          self.fichier_unite=unite
415          self.fichier_err=None
416          import Extensions.jdc_include
417          self.JdC_aux=Extensions.jdc_include.JdC_include
418
419          if f is None:
420              self.fichier_err="Le fichier INCLUDE n est pas defini"
421              self.parent.record_unit(unite,self)
422              raise Exception(self.fichier_err)
423
424          try:
425            self.make_contexte_include(self.fichier_ini ,self.fichier_text)
426            self.parent.record_unit(unite,self)
427          except:
428            l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
429            if self.jdc.appli:
430               self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier inclus",
431                                             message="Ce fichier ne sera pas pris en compte\n"+string.join(l)
432                                            )
433            self.parent.record_unit(unite,self)
434            self.g_context={}
435            self.fichier_err = string.join(l)
436            self.contexte_fichier_init={}
437            raise
438
439       else:
440          # Si le fichier est deja defini on ne reevalue pas le fichier
441          # et on leve une exception si une erreur a été enregistrée
442          self.update_fichier_init(unite)
443          self.fichier_unite=unite
444          if self.fichier_err is not None: raise Exception(self.fichier_err)
445         
446
447 #ATTENTION SURCHARGE : cette methode surcharge celle de Noyau (a garder en synchro)
448   def make_contexte(self,fichier,text):
449     """
450         Cette méthode sert à créer un contexte pour INCLUDE_MATERIAU
451         en interprétant un texte source Python
452         Elle est appelee par la fonction sd_prd d'INCLUDE_MATERIAU
453     """
454     # On supprime l'attribut mat qui bloque l'evaluation du source de l'INCLUDE_MATERIAU
455     # car on ne s'appuie pas sur lui dans EFICAS mais sur l'attribut fichier_ini
456     if hasattr(self,'mat'):del self.mat
457     self.fichier_ini =fichier
458     self.fichier_unite =fichier
459     self.fichier_text=text
460     self.fichier_err=None 
461     self.contexte_fichier_init={}
462     # On specifie la classe a utiliser pour le JDC auxiliaire
463     import Extensions.jdc_include
464     self.JdC_aux=Extensions.jdc_include.JdC_include
465     try:
466        self.make_contexte_include(self.fichier_ini ,self.fichier_text)
467        self.parent.record_unit(self.fichier_unite,self)
468     except:
469        l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
470        self.fichier_err = string.join(l)
471        self.parent.record_unit(self.fichier_unite,self)
472        self.g_context={}
473        self.contexte_fichier_init={}
474        raise
475
476 #ATTENTION SURCHARGE : cette methode surcharge celle de Noyau (a garder en synchro)
477   def update_sdprod(self,cr='non'):
478      # Cette methode peut etre appelee dans EFICAS avec des mots cles de 
479      # la commande modifies. Ceci peut conduire a la construction ou
480      # a la reconstruction d'etapes dans le cas d'INCLUDE ou d'INCLUDE_MATERIAU
481      # Il faut donc positionner le current_step avant l'appel
482      if self.state == "undetermined":return 1
483      CONTEXT.unset_current_step()
484      CONTEXT.set_current_step(self)
485      valid=Validation.V_MACRO_ETAPE.MACRO_ETAPE.update_sdprod(self,cr=cr)
486      CONTEXT.unset_current_step()
487      return valid
488
489 #ATTENTION SURCHARGE: cette methode surcharge celle de Noyau a garder en synchro 
490   def Build_sd(self,nom):
491       """
492            Methode de Noyau surchargee pour poursuivre malgre tout
493            si une erreur se produit pendant la creation du concept produit
494       """
495       try:
496          sd=Noyau.N_MACRO_ETAPE.MACRO_ETAPE.Build_sd(self,nom)
497       except AsException,e:
498          # Une erreur s'est produite lors de la construction du concept
499          # Comme on est dans EFICAS, on essaie de poursuivre quand meme
500          # Si on poursuit, on a le choix entre deux possibilités :
501          # 1. on annule la sd associée à self
502          # 2. on la conserve mais il faut la retourner
503          # On choisit de l'annuler
504          # En plus il faut rendre coherents sdnom et sd.nom
505          self.sd=None
506          self.sdnom=None
507          self.state="unchanged"
508          self.valid=0
509
510       return self.sd
511
512 #ATTENTION SURCHARGE: cette methode surcharge celle de Noyau a garder en synchro 
513   def make_poursuite(self):
514       """ Cette methode est appelée par la fonction sd_prod de la macro POURSUITE
515       """
516       if not hasattr(self,'fichier_ini') :
517          # Si le fichier n'est pas defini on le demande
518          f,text=self.get_file_memo(fic_origine=self.parent.nom)
519          # On memorise le fichier retourne
520          self.fichier_ini = f
521          self.fichier_unite = None
522          self.fichier_text = text
523          self.fichier_err=None
524          import Extensions.jdc_include
525          self.JdC_aux=Extensions.jdc_include.JdC_poursuite
526          self.contexte_fichier_init={}
527
528          if f is None:
529              self.fichier_err="Le fichier POURSUITE n'est pas defini"
530              self.parent.record_unit(None,self)
531              raise Exception(self.fichier_err)
532
533          try:
534            self.make_contexte_include(self.fichier_ini,self.fichier_text)
535            self.parent.record_unit(None,self)
536          except:
537            l=traceback.format_exception_only("Fichier invalide",sys.exc_info()[1])
538            if self.jdc.appli:
539               self.jdc.appli.affiche_alerte("Erreur lors de l'evaluation du fichier poursuite",
540                                             message="Ce fichier ne sera pas pris en compte\n"+string.join(l)
541                                            )
542            self.parent.record_unit(None,self)
543            self.g_context={}
544            self.fichier_err = string.join(l)
545            self.contexte_fichier_init={}
546            raise
547
548       else:
549          # Si le fichier est deja defini on ne reevalue pas le fichier
550          # et on leve une exception si une erreur a été enregistrée
551          self.update_fichier_init(None)
552          if self.fichier_err is not None: raise Exception(self.fichier_err)
553