Salome HOME
d97ec4a23b71269e4bd5494f9d2da65dc3c8e5b5
[tools/eficas.git] / Ihm / I_ETAPE.py
1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2021   EDF R&D
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20 """
21 """
22 # Modules Python
23 from __future__ import absolute_import
24 from __future__ import print_function
25 import sys,re
26 import types
27 from copy import copy
28
29 from Extensions.i18n import tr
30 from Extensions.eficas_exception import EficasException
31
32 # Objet re pour controler les identificateurs Python
33 conceptRE=re.compile(r'[a-zA-Z_]\w*$')
34
35 # import rajoute suite a l'ajout de buildSd --> a resorber
36 import traceback
37 import Noyau
38 from Noyau import N_Exception
39 from Noyau.N_Exception import AsException
40 import Validation
41 # fin import a resorber
42
43 # Modules EFICAS
44 from . import I_MCCOMPO
45 from . import CONNECTOR
46 from Extensions import commande_comm
47
48 class ETAPE(I_MCCOMPO.MCCOMPO):
49
50    def ident(self):
51       return self.nom
52
53    def getSdname(self):
54       #print "SDNAME ",self.reuse,self.sd,self.sd.getName()
55       if CONTEXT.debug : 
56           print(("SDNAME ",  self.reuse,  self.sd,  self.sd.getName()))
57       sdname=''
58       if self.reuse != None:
59         sdname= self.reuse.getName()
60       else:
61         if self.sd:sdname=self.sd.getName()
62       if sdname.find('sansnom') != -1 or sdname.find('SD_') != -1:
63         # dans le cas ou la SD est 'sansnom' ou 'SD_' on retourne la chaine vide
64         return ''
65       return sdname
66
67    def isReentrant(self):
68       """ 
69           Indique si la commande est reentrante
70       """
71       return self.definition.reentrant == 'o' 
72
73    def initModif(self):
74       """
75          Met l'etat de l'etape a : modifie
76          Propage la modification au parent
77       """
78       # initModif doit etre appele avant de realiser une modification
79       # La validite devra etre recalculee apres cette modification
80       # mais dans l'appel a finModif pour preserver l'etat modified
81       # de tous les objets entre temps
82       #print "initModif",self,self.parent
83       self.state = 'modified'
84       if self.parent:
85         self.parent.initModif()
86
87    def finModif(self):
88       """
89           Methode appelee une fois qu'une modification a ete faite afin de 
90           declencher d'eventuels traitements post-modification
91           ex : INCLUDE et POURSUITE
92           Ne pas mettre de traitement qui risque d'induire des recursions (soit a peu pres rien)
93       """
94       CONNECTOR.Emit(self,"valid")
95       if self.parent:
96         self.parent.finModif()
97
98    def nommeSd(self,nom) :
99       """
100           Cette methode a pour fonction de donner un nom (nom) au concept 
101           produit par l'etape (self).
102             - si le concept n'existe pas, on essaye de le creer a condition que l'etape soit valide ET non reentrante)
103             - si il existe dea, on le renomme et on repercute les changements dans les autres etapes    
104           Les valeurs de retour sont :
105             - 0 si le nommage n'a pas pu etre menea son terme,
106             - 1 dans le cas contraire
107       """
108       # Le nom d'un concept doit etre un identificateur Python (toujours vrai ou insuffisant?)
109       if not conceptRE.match(nom):
110          return 0, tr("Un nom de concept doit etre un identificateur Python")
111
112       # pour eviter que le nom du concept soit le nom de la classe --> souci pour utiliser le concept
113       if (nom == self.definition.nom) : return  (0, tr("un concept de type ")+ nom + tr(" ne peut pas se nommer ") +  nom)
114       if ( nom in dir(self.jdc.cata)) : return (0, nom + tr("est un not reserve"))
115       #if (not isinstance(nom,str)) : return (0, tr("Le nom ") + nom + tr(" est un mot reserve"))
116       #if len(nom) > 8 and self.jdc.definition.code == 'ASTER':
117       #  return 0, tr("Nom de concept trop long (maxi 8 caracteres)")
118
119       self.initModif()
120       #
121       # On verifie d'abord si les mots cles sont valides
122       #
123       if not self.isValid(sd='non') : return 0,"Nommage du concept refuse : l'operateur n'est pas valide"
124       #
125       # Cas particulier des operateurs obligatoirement reentrants
126       # plus de concept reentrant (pour Aster)
127       #
128       if self.definition.reentrant == 'o':
129         self.sd = self.reuse = self.jdc.getSdAvantEtape(nom,self)
130         if self.sd != None :
131           self.sdnom=self.sd.nom
132           self.finModif()
133           return 1, tr("Concept existant")
134         else:
135           return 0, tr("Operateur reentrant mais concept non existant")
136       #
137       # Cas particulier des operateurs facultativement reentrants
138       #
139       old_reuse=None
140       if self.definition.reentrant == 'f' :
141         sd = self.jdc.getSdAvantEtape(nom,self)
142         if sd != None :
143           if isinstance(sd,self.getType_produit()) :
144              self.sd = self.reuse = sd
145              self.sdnom = sd.nom
146              self.finModif()
147              return 1, tr("Operateur reentrant et concept existant trouve")
148           else:
149              return 0, tr("Concept deja existant et de mauvais type")
150         else :
151           # il faut enlever le lien vers une SD existante car si on passe ici
152           # cela signifie que l'operateur n'est pas utilise en mode reentrant.
153           # Si on ne fait pas cela, on risque de modifier une SD produite par un autre operateur
154           if self.reuse :
155              old_reuse=self.reuse
156              self.sd = self.reuse = self.sdnom = None
157       #
158       # On est dans le cas ou l'operateur n'est pas reentrant ou est facultativement reentrant
159       # mais est utilise en mode non reentrant
160       #
161       if self.sd == None :
162           #Pas de concept produit preexistant
163           if self.parent.getSdAutourEtape(nom,self):
164             # Un concept de ce nom existe dans le voisinage de l'etape courante
165             # On retablit l'ancien concept reentrant s'il existait
166             if old_reuse:
167                self.sd=self.reuse=old_reuse
168                self.sdnom=old_reuse.nom
169             return 0, tr("Nommage du concept refuse : un concept de meme nom existe deja")
170           else:
171             # Il n'existe pas de concept de ce nom dans le voisinage de l'etape courante
172             # On peut donc creer le concept retourne
173             # Il est cree sans nom mais enregistre dans la liste des concepts existants
174             try:
175                self.getSdProd()
176                # Renommage du concept : Il suffit de changer son attribut nom pour le nommer
177                self.sd.nom = nom
178                self.sdnom=nom
179                self.parent.sdsDict[nom]=self.sd
180                self.parent.updateConceptAfterEtape(self,self.sd)
181                self.finModif()
182                return 1, tr("Nommage du concept effectue")
183             except:
184                return 0, tr("Nommage impossible %s", str(sys.exc_info()[1]))
185       else :
186           #Un concept produit preexiste
187           old_nom=self.sd.nom
188           if old_nom.find('sansnom') :
189             # Dans le cas ou old_nom == sansnom, isValid retourne 0 alors que ...
190             # par contre si le concept existe et qu'il s'appelle sansnom c'est que l'etape est valide
191             # on peut donc le nommer sans test prealable
192             if self.parent.getSdAutourEtape(nom,self):
193               return 0, tr("Nommage du concept refuse : un concept de meme nom existe deja")
194             else:
195               # Renommage du concept : Il suffit de changer son attribut nom pour le nommer
196               self.sd.nom=nom
197               self.sdnom=nom
198               self.parent.updateConceptAfterEtape(self,self.sd)
199               self.finModif()
200               return 1, tr("Nommage du concept effectue")
201           if self.isValid() :
202             # Normalement l appel de isValid a mis a jour le concept produit (son type)
203             # Il suffit de specifier l attribut nom de sd pour le nommer si le nom n est pas
204             # deja attribue
205             if self.parent.getSdAutourEtape(nom,self):
206               return 0, tr("Nommage du concept refuse : un concept de meme nom existe deja")
207             else:
208               # Renommage du concept : Il suffit de changer son attribut nom pour le nommer
209               self.sd.nom=nom
210               self.sdnom=nom
211               self.parent.updateConceptAfterEtape(self,self.sd)
212               self.finModif()
213               return 1, tr("Nommage du concept effectue")
214           else:
215             # Normalement on ne devrait pas passer ici
216             return 0, 'Normalement on ne devrait pas passer ici'
217
218    def getSdprods(self,nom_sd):
219       """ 
220          Fonction : retourne le concept produit par l etape de nom nom_sd
221          s il existe sinon None
222       """
223       if self.sd:
224         if self.sd.nom == nom_sd:return self.sd
225
226    def active(self):
227       """
228           Rend l'etape courante active.
229           Il faut ajouter la sd si elle existe au contexte global du JDC
230           et a la liste des sd
231       """
232       if self.actif:return
233       self.actif = 1
234       self.initModif()
235       if self.sd :
236         try:
237           self.jdc.appendSdProd(self.sd)
238         except:
239           pass
240       CONNECTOR.Emit(self,"add",None)
241       CONNECTOR.Emit(self,"valid")
242
243    def inactive(self):
244       """
245           Rend l'etape courante inactive
246           Il faut supprimer la sd du contexte global du JDC
247           et de la liste des sd
248       """
249       self.actif = 0
250       self.initModif()
251       if self.sd :
252          self.jdc.delSdprod(self.sd)
253          self.jdc.deleteConceptAfterEtape(self,self.sd)
254       CONNECTOR.Emit(self,"supp",None)
255       CONNECTOR.Emit(self,"valid")
256
257    def controlSdprods(self,d):
258       """
259           Cette methode doit verifier que ses concepts produits ne sont pas
260           deja definis dans le contexte
261           Si c'est le cas, les concepts produits doivent etre supprimes
262       """
263       print ("controlSdprods etape",d.keys(),self.sd and self.sd.nom,self.nom)
264       if self.sd:
265         if self.sd.nom in d :
266            # Le concept est deja defini
267            if self.reuse and self.reuse is d[self.sd.nom]:
268               # Le concept est reutilise : situation normale
269               pass
270            else:
271               # Redefinition du concept, on l'annule
272               #XXX on pourrait simplement annuler son nom pour conserver les objets
273               # l'utilisateur n'aurait alors qu'a renommer le concept (faisable??)
274               self.initModif()
275               sd=self.sd
276               self.sd=self.reuse=self.sdnom=None
277               #supprime les references a sd dans les etapes suivantes
278               self.parent.deleteConceptAfterEtape(self,sd)
279               self.finModif()
280
281    def supprimeSdprod(self,sd):
282       """
283          Supprime le concept produit sd s'il est produit par l'etape
284       """
285       if sd is not self.sd:return
286       if self.sd != None :
287          self.initModif()
288          self.parent.delSdprod(sd)
289          self.sd=None
290          self.finModif()
291          self.parent.deleteConcept(sd)
292
293    def supprimeSdProds(self):
294       """ 
295             Fonction:
296             Lors d'une destruction d'etape, detruit tous les concepts produits
297             Un operateur n a qu un concept produit 
298             Une procedure n'en a aucun
299             Une macro en a en general plus d'un
300       """
301       self.deleteRef()
302       #print "supprimeSdProds",self
303       if self.reuse is self.sd :return
304       # l'etape n'est pas reentrante
305       # le concept retourne par l'etape est a supprimer car il etait 
306       # cree par l'etape
307       if self.sd != None :
308          self.parent.delSdprod(self.sd)
309          self.parent.deleteConcept(self.sd)
310
311    def close(self):
312       return
313
314    def updateConcept(self,sd):
315       for child in self.mcListe :
316           child.updateConcept(sd)
317
318    def deleteConcept(self,sd):
319       """ 
320           Inputs :
321              - sd=concept detruit
322           Fonction :
323           Mettre a jour les mots cles de l etape et eventuellement 
324           le concept produit si reuse
325           suite a la disparition du concept sd
326           Seuls les mots cles simples MCSIMP font un traitement autre 
327           que de transmettre aux fils
328       """
329       if self.reuse and self.reuse == sd:
330         self.sd=self.reuse=None
331         self.initModif()
332       for child in self.mcListe :
333         child.deleteConcept(sd)
334
335    def replaceConcept(self,old_sd,sd):
336       """
337           Inputs :
338              - old_sd=concept remplace
339              - sd = nouveau concept 
340           Fonction :
341           Mettre a jour les mots cles de l etape et eventuellement
342           le concept produit si reuse
343           suite au remplacement  du concept old_sd
344       """
345       if self.reuse and self.reuse == old_sd:
346         self.sd=self.reuse=sd
347         self.initModif()
348       for child in self.mcListe :
349         child.replaceConcept(old_sd,sd)
350
351    def resetContext(self):
352       pass
353
354    def getNomsSdOperReentrant(self):
355       """ 
356           Retourne la liste des noms de concepts utilisesa l'interieur de la commande
357           qui sont du type que peut retourner cette commande 
358       """
359       liste_sd = self.getSd_utilisees()
360       l_noms = []
361       if type(self.definition.sd_prod) == types.FunctionType:
362         d=self.creeDictValeurs(self.mcListe)
363         try:
364           classe_sd_prod = self.definition.sd_prod(*(), **d)
365         except:
366           return []
367       else:
368         classe_sd_prod = self.definition.sd_prod
369       for sd in liste_sd :
370         if sd.__class__ is classe_sd_prod : l_noms.append(sd.nom)
371       l_noms.sort()
372       return l_noms
373
374    def getGenealogiePrecise(self):
375       return [self.nom]
376
377    def getNomDsXML(self):
378      # en xml on a un choice 
379      index=0
380      for e in self.parent.etapes :
381          if e == self : break
382          if e.nom == self.nom : index+=1
383      nomDsXML = self.nom + "[" + str(index) + "]"
384      return nomDsXML
385
386
387    def getGenealogie(self):
388       """ 
389           Retourne la liste des noms des ascendants de l'objet self
390           en s'arretant a la premiere ETAPE rencontree
391       """
392       return [self.nom]
393
394    def verifExistenceSd(self):
395      """
396         Verifie que les structures de donnees utilisees dans self existent bien dans le contexte
397         avant etape, sinon enleve la referea ces concepts
398      """
399      #print "verifExistenceSd",self.sd
400      for motcle in self.mcListe :
401          motcle.verifExistenceSd()
402
403    def updateMcGlobal(self):
404      """
405         Met a jour les mots cles globaux enregistres dans l'etape
406         et dans le jdc parent.
407         Une etape ne peut pas etre globale. Elle se contente de passer
408         la requete a ses fils apres avoir reinitialise le dictionnaire 
409         des mots cles globaux.
410      """
411      self.mc_globaux={}
412      I_MCCOMPO.MCCOMPO.updateMcGlobal(self)
413
414    def updateConditionBloc(self):
415      """
416         Realise l'update des blocs conditionnels fils de self
417      """
418      self._updateConditionBloc()
419
420    def getObjetCommentarise(self,format):
421       """
422           Cette methode retourne un objet commande commentarisee
423           representant la commande self
424       """
425       import generator
426       g=generator.plugins[format]()
427       texte_commande = g.gener(self,format='beautifie')
428       # Il faut enlever la premiere ligne vide de texte_commande que
429       # rajoute le generator
430       # on construit l'objet COMMANDE_COMM repesentatif de self mais non
431       # enregistre dans le jdc (pas ajoute dans jdc.etapes)
432       parent=self.parent
433       pos=self.parent.etapes.index(self)
434       # on ajoute une fin à la commande pour pouvoir en commenter 2
435       texte_commande+='\nFin Commentaire'
436       commande_comment = commande_comm.COMMANDE_COMM(texte=texte_commande,
437                                                      reg='non',
438                                                      parent=parent)
439       self.parent.suppEntite(self)
440       parent.addEntite(commande_comment,pos)
441
442       return commande_comment
443
444    def modified(self):
445       """Le contenu de l'etape (mots cles, ...) a ete modifie"""
446       if self.nom=="DETRUIRE":
447         self.parent.controlContextApres(self)
448
449
450      
451 #ATTENTION SURCHARGE: a garder en synchro ou a reintegrer dans le Noyau
452    def buildSd(self,nom):
453       """
454            Methode de Noyau surchargee pour poursuivre malgre tout
455            si une erreur se produit pendant la creation du concept produit
456       """
457       try:
458          sd=Noyau.N_ETAPE.ETAPE.buildSd(self,nom)
459       except AsException as e :
460          # Une erreur s'est produite lors de la construction du concept
461          # Comme on est dans EFICAS, on essaie de poursuivre quand meme
462          # Si on poursuit, on a le choix entre deux possibilites :
463          # 1. on annule la sd associee a self
464          # 2. on la conserve mais il faut la retourner
465          # En plus il faut rendre coherents sdnom et sd.nom
466          self.sd=None
467          self.sdnom=None
468          self.state="unchanged"
469          self.valid=0
470
471       return self.sd
472
473 #ATTENTION SURCHARGE: cette methode doit etre gardee en synchronisation avec Noyau
474    def makeRegister(self):
475       """
476          Initialise les attributs jdc, id, niveau et realise les
477          enregistrements necessaires
478          Pour EFICAS, on tient compte des niveaux
479          Surcharge la methode makeRegister du package Noyau
480       """
481       if self.parent :
482          self.jdc = self.parent.getJdcRoot()
483          self.id=   self.parent.register(self)
484          self.UserError=self.jdc.UserError
485          if self.definition.niveau :
486             # La definition est dans un niveau. En plus on
487             # l'enregistre dans le niveau
488             self.nom_niveau_definition = self.definition.niveau.nom
489             self.niveau = self.parent.dict_niveaux[self.nom_niveau_definition]
490             self.niveau.register(self)
491          else:
492             # La definition est au niveau global
493             self.nom_niveau_definition = 'JDC'
494             self.niveau=self.parent
495       else:
496          self.jdc = self.parent =None
497          self.id=None
498          self.niveau=None
499          self.UserError="UserError"
500
501    def report(self):
502      cr= Validation.V_ETAPE.ETAPE.report(self)
503      #rafraichisst de la validite de l'etape (probleme avec l'ordre dans les macros : etape puis mots cles)
504      self.isValid()
505      if not self.isValid() and self.nom == "INCLUDE" :
506         self.cr.fatal('Etape : {} ligne : {}  {}'.format(self.nom, self.appel[0],  tr("\n   Include Invalide. \n  ne sera pas pris en compte")))
507      return cr
508