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