]> SALOME platform Git repositories - tools/eficas.git/blob - Noyau/N_MCCOMPO.py
Salome HOME
reindent + copyright + merge manuel avec la V9_dev sauf repertoires metier
[tools/eficas.git] / Noyau / N_MCCOMPO.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     Ce module contient la classe MCCOMPO qui sert à factoriser les comportements
23     des OBJECT composites
24 """
25
26 from __future__ import absolute_import
27 from __future__ import print_function
28 from copy import copy
29 from . import N_OBJECT
30
31
32 class MCCOMPO(N_OBJECT.OBJECT):
33
34     """
35        Classe support d'un OBJECT composite
36
37     """
38
39     def buildMc(self):
40         """
41             Construit la liste des sous-entites du MCCOMPO
42             à partir du dictionnaire des arguments (valeur)
43         """
44
45         #import traceback
46         #traceback.print_stack()
47         #print(("MCCOMPO.buildMc _____________________________________", self.nom))
48         if CONTEXT.debug:
49             print(("MCCOMPO.buildMc ", self.nom))
50         # Dans la phase de reconstruction args peut contenir des mots-clés
51         # qui ne sont pas dans le dictionnaire des entites de definition (self.definition.entites)
52         # de l'objet courant (self)
53         # mais qui sont malgré tout des descendants de l'objet courant
54         # (petits-fils, ...)
55         args = self.valeur
56         #print ('MCCOMPO___________________', self.valeur)
57         if args == None: args = {}
58         mcListe = []
59
60
61         # On recopie le dictionnaire des arguments pour protéger l'original des
62         # delete (del args[k])
63         args = args.copy()
64
65         # Phase 1:
66         # On construit les sous entites presentes ou obligatoires
67         # 1- les entites présentes dans les arguments et dans la définition
68         # 2- les entités non présentes dans les arguments, présentes dans la définition avec un défaut
69         # Phase 1.1 : on traite d'abord les SIMP pour enregistrer les mots cles
70         # globaux
71         if not hasattr(self, 'dicoPyxbDeConstruction') : self.dicoPyxbDeConstruction = {}
72         for k, v in list(self.definition.entites.items()):
73             if v.label != 'SIMP':
74                 continue
75             if k in args or v.statut == 'o':
76                 #
77                 # Creation par appel de la methode __call__ de la definition de la sous entite k de self
78                 # si une valeur existe dans args ou est obligatoire (generique si toutes les
79                 # entites ont l attribut statut )
80                 #
81                 if self.dicoPyxbDeConstruction and  k in self.dicoPyxbDeConstruction :
82                     objPyxbDeConstruction=self.dicoPyxbDeConstruction[k]
83                     del self.dicoPyxbDeConstruction[k]
84                 else :
85                     objPyxbDeConstruction=None
86                 #print (args.get(k, None))
87                 objet = v(val=args.get(k, None), nom=k, parent=self,objPyxbDeConstruction=objPyxbDeConstruction)
88                 mcListe.append(objet)
89                 # Si l'objet a une position globale on l'ajoute aux listes
90                 # correspondantes
91                 if hasattr(objet.definition, 'position'):
92                     if objet.definition.position == 'global':
93                         self.append_mc_global(objet)
94                     if objet.definition.position == 'reCalculeEtape':
95                         #print ('-------------------------- rencontre reCalculeEtape: ', objet.nom)
96                         self.append_mc_global_avecRecalcule(objet)
97                     elif objet.definition.position == 'global_jdc':
98                         self.append_mc_global_jdc(objet)
99             if k in args:
100                 del args[k]
101
102         # Phase 1.2 : on traite les autres entites que SIMP
103         # FACT ou listeDeFAct en fait car un BLOC ne peut etre present dans les args
104         for k, v in list(self.definition.entites.items()):
105             if v.label == 'SIMP': continue
106             if k in args or v.statut == 'o':
107                 #print ('construit', k)
108                 #
109                 # Creation par appel de la methode __call__ de la definition de la sous entite k de self
110                 # si une valeur existe dans args ou est obligatoire (generique si toutes les
111                 # entites ont l attribut statut )
112                 #
113                 if self.dicoPyxbDeConstruction and  k in self.dicoPyxbDeConstruction :
114                     dicoPyxbDeConstruction=self.dicoPyxbDeConstruction[k]
115                     del self.dicoPyxbDeConstruction[k]
116                 else :
117                     dicoPyxbDeConstruction=None
118                 objet = v(val=args.get(k, None), nom=k, parent=self,dicoPyxbDeConstruction=dicoPyxbDeConstruction)
119                 mcListe.append(objet)
120             if k in args:
121                 del args[k]
122
123         # Phase 2:
124         # On construit les objets (en général, blocs) conditionnés par les mots-clés précédemment créés.
125         # A ce stade, mcListe ne contient que les fils de l'objet courant
126         # args ne contient plus que des mots-clés qui n'ont pas été attribués car ils sont
127         #      à attribuer à des blocs du niveau inférieur ou bien sont des mots-clés erronés
128         for k, v in list(self.definition.entites.items()):
129             if v.label != 'BLOC': continue
130             #PNPN on recalcule dico_valeurs dans le for
131             # pour les globaux imbriques (exple Telemac Advection)
132             # avant le calcul etait avant le for
133             dico_valeurs = self.creeDictCondition(mcListe, condition=1)
134             globs = self.jdc and self.jdc.condition_context or {}
135             if v.verifPresence(dico_valeurs, globs):
136                 #print ('appel construit bloc', k, 'avec', args, 'a partir de', self.nom )
137                     # Si le bloc existe :
138                     #        1- on le construit
139                     #        2- on l'ajoute à mcListe
140                     #        3- on récupère les arguments restant
141                     # 4- on reconstruit le dictionnaire équivalent à mcListe
142                 bloc = v(nom=k, val=args, parent=self,dicoPyxbDeConstruction=self.dicoPyxbDeConstruction)
143                 mcListe.append(bloc)
144                 args = bloc.reste_val
145                 #print ('les args deviennent ', args)
146                 # On ne recalcule pas le contexte car on ne tient pas compte des blocs
147                 # pour évaluer les conditions de présence des blocs
148                 # dico_valeurs = self.creeDictValeurs(mcListe)
149
150         # On conserve les arguments superflus dans l'attribut reste_val
151         # les reste_val des blocs  vont contenir trop de MC
152         # car ils sont appeles avec tous les MC de leur niveau qui n ont pas ete consommes
153         # et le reste_val n est pas remis a jour
154         # est-ce un pb ? a priori non
155         self.reste_val = args
156         #print ('self.reste_val de ', self.nom, self.reste_val)
157         # On ordonne la liste ainsi créée suivant l'ordre du catalogue
158         # (utile seulement pour IHM graphique)
159         mcListe = self.ordonneListe(mcListe)
160         # on retourne la liste ainsi construite
161         if self.jdc  : self.cata=self.jdc.cata
162         else : self.cata = None
163         self.buildObjPyxb(mcListe)
164         #print ('______________________________________ fin ', self.nom)
165         return mcListe
166
167     def buildMcApresGlobalEnSuppression(self):
168         blocsDejaLa=[]
169         for mc in self.mcListe :
170             if mc.nature == 'MCBLOC' : blocsDejaLa.append(mc)
171         for mc in  blocsDejaLa :
172             dico_valeurs = self.creeDictCondition(self.mcListe, condition=1)
173             globs = self.jdc and self.jdc.condition_context or {}
174             defBloc = mc.definition
175             if not (defBloc.verifPresence(dico_valeurs, globs)):
176                 self.suppEntite(mc)
177
178
179     def reConstruitResteVal(self):
180     # normal que apres buildMcApresGlobalEnCreation les reste_val ne soient pas corrects
181         for mc in self.mcListe :
182             if mc.nom in self.reste_val :
183                 del self.reste_val[mc.nom]
184             if mc.nature == 'MCBLOC' :
185                 ancetre=mc.parent
186                 for mcFDuMc in mc.mcListe :
187                     while ancetre.nature == 'MCBLOC' :
188                         ancetre=ancetre.parent
189                         if mcFDuMc.nom in ancetre.reste_val : del ancetre.reste_val[mcFDuMc.nom]
190             if mc.nature == 'MCSIMP' : continue
191             if mc.nature == 'MCList' :
192                 for mcObj in mc.data :
193                     mcObj.reConstruitResteVal()
194             else :
195                 mc.reConstruitResteVal()
196
197
198     def buildMcApresGlobalEnCreation(self):
199         nouveau_args = self.reste_val
200         blocsDejaLa=[]
201         for mc in self.mcListe :
202             if mc.nature == 'MCBLOC' : blocsDejaLa.append(mc.nom)
203         for k, v in list(self.definition.entites.items()):
204             if v.label != 'BLOC': continue
205             if k in blocsDejaLa : continue
206             dico_valeurs = self.creeDictCondition(self.mcListe, condition=1)
207             globs = self.jdc and self.jdc.condition_context or {}
208             if v.verifPresence(dico_valeurs, globs):
209                 bloc = v(nom=k, val=nouveau_args, parent=self,dicoPyxbDeConstruction=self.dicoPyxbDeConstruction)
210                 if bloc :
211                     self.mcListe.append(bloc)
212                     bloc.addObjPyxb(self.chercheIndiceDsLeContenu(bloc))
213                     nouveau_args = self.reste_val
214                     self.reste_val = bloc.reste_val
215
216
217     def ordonneListe(self, mcListe):
218         """
219            Ordonne la liste suivant l'ordre du catalogue.
220            Seulement pour IHM graphique
221         """
222         if self.jdc and self.jdc.cata_ordonne_dico != None:
223             liste_noms_mc_ordonnee = self.getListeMcOrdonneeBrute(
224                 self.getGenealogie(), self.jdc.cata_ordonne_dico)
225             return self.ordonneListeMc(mcListe, liste_noms_mc_ordonnee)
226         else:
227             return mcListe
228
229     def creeDictValeurs(self, liste=[], condition=0):
230         """
231           Cette méthode crée un contexte (sous la forme d'un dictionnaire)
232           à partir des valeurs des mots clés contenus dans l'argument liste.
233           L'opération consiste à parcourir la liste (d'OBJECT) et à la
234           transformer en un dictionnaire dont les clés sont les noms des
235           mots clés et les valeurs dépendent du type d'OBJECT.
236           Ce dictionnaire servira de liste d'arguments d'appel pour les
237           fonctions sd_prod de commandes et ops de macros ou de contexte
238           d'évaluation des conditions de présence de BLOC.
239
240           Si l'argument condition de la méthode vaut 1, on ne
241           remonte pas les valeurs des mots clés contenus dans des blocs
242           pour eviter les bouclages.
243
244           Cette méthode réalise les opérations suivantes en plus de transformer
245           la liste en dictionnaire :
246
247              - ajouter tous les mots-clés non présents avec la valeur None
248              - ajouter tous les mots-clés globaux (attribut position = 'global'
249                et 'global_jdc')
250
251           L'argument liste est, en général, une mcListe en cours de
252           construction, contenant les mots-clés locaux et les blocs déjà créés.
253
254         """
255         dico = {}
256         for v in liste:
257             if v.isBLOC():
258                 # Si v est un BLOC, on inclut ses items dans le dictionnaire
259                 # représentatif du contexte. Les blocs sont retournés par getValeur
260                 # sous la forme d'un dictionnaire : les mots-clés fils de blocs sont
261                 # donc remontés au niveau du contexte.
262                 if not condition:
263                     dadd = v.getValeur()
264                     assert intersection_vide(dico, dadd)
265                     dico.update(dadd)
266             else:
267                 assert not v.nom in dico, "deja vu : %s" % v.nom
268                 dico[v.nom] = v.getValeur()
269
270         # On rajoute tous les autres mots-clés locaux possibles avec la valeur
271         # par défaut ou None
272         # Pour les mots-clés facteurs, on ne traite que ceux avec statut défaut ('d')
273         # et caché ('c')
274         # On n'ajoute aucune information sur les blocs. Ils n'ont pas de défaut seulement
275         # une condition.
276         # XXX remplacer le not has_key par un dico différent et faire dico2.update(dico)
277         #    ce n'est qu'un pb de perf
278         for k, v in list(self.definition.entites.items()):
279             if not k in  dico :
280                 if v.label == 'SIMP':
281                         # Mot clé simple
282                     dico[k] = v.defaut
283                 elif v.label == 'FACT':
284                     if v.statut in ('c', 'd'):
285                         # Mot clé facteur avec défaut ou caché provisoire
286                         dico[k] = v(val=None, nom=k, parent=self)
287                         # On demande la suppression des pointeurs arrieres
288                         # pour briser les eventuels cycles
289                         dico[k].supprime()
290                     else:
291                         dico[k] = None
292         # A ce stade on a rajouté tous les mots-clés locaux possibles (fils directs) avec leur
293         # valeur par défaut ou la valeur None
294
295         # On rajoute les mots-clés globaux sans écraser les clés existantes
296         dico_mc = self.rechercheMcGlobaux()
297         dico_mc.update(dico)
298         dico = dico_mc
299
300         return dico
301
302     def creeDictToutesValeurs(self):
303         """Semblable à `creeDictValeurs(liste=self.mcListe)` en supprimant les
304         valeurs None."""
305         dico = self.creeDictValeurs(self.mcListe, condition=0)
306         dico = dict([(k, v) for k, v in list(dico.items()) if v is not None])
307         return dico
308
309     def creeDictCondition(self, liste=[], condition=0):
310         """
311             Methode pour construire un contexte qui servira dans l'évaluation
312             des conditions de présence de blocs. Si une commande a un concept
313             produit réutilisé, on ajoute la clé 'reuse'
314         """
315         dico = self.creeDictValeurs(liste, condition=1)
316         # On ajoute la cle "reuse" pour les MCCOMPO qui ont un attribut reuse. A destination
317         # uniquement des commandes. Ne devrait pas etre dans cette classe mais
318         # dans une classe dérivée
319         if not 'reuse' in dico and hasattr(self, 'reuse'):
320             dico['reuse'] = self.reuse
321         return dico
322
323     def rechercheMcGlobaux(self):
324         """
325             Retourne la liste des mots-clés globaux de l'étape à laquelle appartient self
326             et des mots-clés globaux du jdc
327         """
328         etape = self.getEtape()
329         if etape:
330             dict_mc_globaux_fac = self.rechercheMcGlobauxFacultatifs()
331             for k, v in list(etape.mc_globaux.items()):
332                 dict_mc_globaux_fac[k] = v.getValeur()
333             if self.jdc:
334                 for k, v in list(self.jdc.mc_globaux.items()):
335                     dict_mc_globaux_fac[k] = v.getValeur()
336             return dict_mc_globaux_fac
337         else:
338             return {}
339
340     def rechercheMcGlobauxFacultatifs(self):
341         """
342             Cette méthode interroge la définition de self et retourne la liste des mots-clés fils
343             directs de self de type 'global'.
344             position='global' n'est donc possible (et n'a de sens) qu'au plus haut niveau.
345             du coup ici on ajoute les globaux de l etape qui sont dans mc_recalculeEtape
346         """
347         #print ('je passe par ici', self.nom)
348         dico = {}
349         etape = self.getEtape()
350         if not etape:
351             return {}
352         for k, v in list(etape.definition.entites.items()):
353             if v.label != 'SIMP': continue
354             if v.position == 'local': continue
355             if v.position == 'inGetAttribut': continue
356             if v.position == 'reCalculeEtape': continue
357             if v.statut == 'o': continue
358             obj = v(val=None, nom=k, parent=etape)
359             dico[k] = obj.getValeur()
360         return dico
361
362     def supprime(self):
363         """
364            Méthode qui supprime toutes les références arrières afin que l'objet puisse
365            etre correctement détruit par le garbage collector
366         """
367         N_OBJECT.OBJECT.supprime(self)
368         for child in self.mcListe:
369             child.supprime()
370
371     def __getitem__(self, key):
372         """
373            Cette méthode retourne la valeur d'un sous mot-clé (key)
374         """
375         return self.getMocle(key)
376
377     def getMocle(self, key):
378         """
379             Retourne la valeur du sous mot-clé key
380             Ce sous mot-clé peut exister, avoir une valeur par defaut ou etre
381             dans un BLOC fils de self
382         """
383         # on cherche dans les mots cles presents, le mot cle de nom key
384         # s'il est là on retourne sa valeur (méthode getVal)
385         for child in self.mcListe:
386             if child.nom == key:
387                 return child.getValeur()
388         #  Si on n a pas trouve de mot cle present on retourne le defaut
389         #  eventuel pour les mots cles accessibles dans la definition
390         #  a ce niveau
391         try:
392             d = self.definition.entites[key]
393             if d.label == 'SIMP':
394                 return d.defaut
395             elif d.label == 'FACT':
396                 # il faut construire les objets necessaires pour
397                 # evaluer les conditions des blocs eventuels (a faire)
398                 if d.statut == 'o':
399                     return None
400                 if d.statut != 'c' and d.statut != 'd':
401                     return None
402                 else:
403                     return d(val=None, nom=key, parent=self)
404         except KeyError:
405             # le mot cle n est pas defini a ce niveau
406             pass
407         #  Si on a toujours rien trouve, on cherche dans les blocs presents
408         #  On suppose que tous les blocs possibles ont ete crees meme ceux
409         #  induits par un mot cle simple absent avec defaut (???)
410         for mc in self.mcListe:
411             if not mc.isBLOC():
412                 continue
413             try:
414                 return mc.getMocle(key)
415             except:
416                 # On n a rien trouve dans ce bloc, on passe au suivant
417                 pass
418         #  On a rien trouve, le mot cle est absent.
419         #  On leve une exception
420         raise IndexError("Le mot cle %s n existe pas dans %s" % (key, self))
421
422     def getChild(self, name, restreint='non'):
423         """
424             Retourne le fils de self de nom name ou None s'il n'existe pas
425             Si restreint vaut oui : ne regarde que dans la mcListe
426             Si restreint vaut non : regarde aussi dans les entites possibles
427             avec defaut (Ce dernier cas n'est utilisé que dans le catalogue)
428         """
429         for v in self.mcListe:
430             if v.nom == name:
431                 return v
432         if restreint == 'non':
433             try:
434                 entite = self.definition.entites[name]
435                 if entite.label == 'SIMP' or (entite.label == 'FACT' and entite.statut in ('c', 'd')):
436                     return entite(None, name, None)
437             except:
438                 pass
439
440         return None
441
442     def getChildOrChildInBloc(self, name, restreint='non'):
443     # cherche dans les fils et les fils des blocs
444     # tout est base sur le fait que deux freres ne peuvent pas avoir le meme nom
445     # dans des blocs non exclusifs, sinon le .comm n est pas du python valide
446         for v in self.mcListe:
447             if v.nom == name: return v
448         if restreint == 'non':
449             try:
450                 entite = self.definition.entites[name]
451                 if entite.label == 'SIMP' or (entite.label == 'FACT' and entite.statut in ('c', 'd')):
452                     return entite(None, name, None)
453             except:
454                 pass
455         for v in self.mcListe:
456             if v.nature == 'MCBLOC' :
457                 petitFils=v.getChildOrChildInBloc(name, restreint) 
458                 if petitFils !=None :  return petitFils
459         return None
460
461     def append_mc_global_avecRecalcule(self, mc):
462         etape = self.getEtape()
463         if etape:
464             nom = mc.nom
465             if not(nom in etape.mc_globaux) :
466                 etape.doitEtreRecalculee = True
467             etape.mc_globaux[nom] = mc
468             #print ('ajout de nom', mc.nom, 'ds les mc_globaux de', etape.nom)
469
470
471     def append_mc_global(self, mc):
472         """
473            Ajoute le mot-clé mc à la liste des mots-clés globaux de l'étape
474         """
475         etape = self.getEtape()
476         if etape:
477             nom = mc.nom
478             etape.mc_globaux[nom] = mc
479
480     def append_mc_global_jdc(self, mc):
481         """
482             Ajoute le mot-clé mc à la liste des mots-clés globaux du jdc
483         """
484         nom = mc.nom
485         self.jdc.mc_globaux[nom] = mc
486
487     def copy(self):
488         """ Retourne une copie de self """
489         objet = self.makeobjet()
490         # attention !!! avec makeobjet, objet a le meme parent que self
491         # ce qui n'est pas du tout bon dans le cas d'une copie !!!!!!!
492         # le pb c'est qu'on vérifie ensuite quel parent avait l'objet
493         # Il me semble preferable de changer le parent a la fin quand la copie
494         # est acceptee
495         objet.valeur = copy(self.valeur)
496         objet.val = copy(self.val)
497         objet.mcListe = []
498         for obj in self.mcListe:
499             new_obj = obj.copy()
500             new_obj.reparent(objet)
501             objet.mcListe.append(new_obj)
502         return objet
503
504     def reparent(self, parent):
505         """
506             Cette methode sert a reinitialiser la parente de l'objet
507         """
508         self.parent = parent
509         self.jdc = parent.getJdcRoot()
510         self.etape = parent.etape
511         for mocle in self.mcListe:
512             mocle.reparent(self)
513
514     def getSd_utilisees(self):
515         """
516           Retourne la liste des concepts qui sont utilisés à l'intérieur de self
517           ( comme valorisation d'un MCS)
518         """
519         l = []
520         for child in self.mcListe:
521             l.extend(child.getSd_utilisees())
522         return l
523
524     def getSd_mcs_utilisees(self):
525         """
526             Retourne la ou les SD utilisée par self sous forme d'un dictionnaire :
527               - Si aucune sd n'est utilisée, le dictionnaire est vide.
528               - Sinon, les clés du dictionnaire sont les mots-clés derrière lesquels on
529                 trouve des sd ; la valeur est la liste des sd attenante.
530                 Exemple ::
531
532                   { 'VALE_F': [ <Cata.cata.fonction_sdaster instance at 0x9419854>,
533                                 <Cata.cata.fonction_sdaster instance at 0x941a204> ],
534                     'MODELE': [<Cata.cata.modele instance at 0x941550c>] }
535         """
536         dico = {}
537         for child in self.mcListe:
538             daux = child.getSd_mcs_utilisees()
539             for cle in daux:
540                 dico[cle] = dico.get(cle, [])
541                 dico[cle].extend(daux[cle])
542         return dico
543
544     def getMcsWithCo(self, co):
545         """
546            Cette methode retourne l'objet MCSIMP fils de self
547            qui a le concept co comme valeur.
548            En principe, elle ne doit etre utilisee que pour les concepts
549            instances de la classe CO
550         """
551         l = []
552         for child in self.mcListe:
553             l.extend(child.getMcsWithCo(co))
554         return l
555
556     def getAllCo(self):
557         """
558            Cette methode retourne tous les concepts instances de CO
559         """
560         l = []
561         for child in self.mcListe:
562             l.extend(child.getAllCo())
563         return l
564
565
566     #def getSdCreeParObjetAvecFiltre(self,objetAssdMultiple):
567     # est-ce que si on est bloc, il faut passer à parent ?
568     # ou prevoir une autre fonction qui tienne compte de cela
569     # ou prevoir un xpath
570     #   classeAChercher = objetAssdMultiple.definition.type
571     #   filtre  = objetAssdMultiple.definition.filtre
572     #   print ('getSdCreeParObjetAvecFiltre', classeAChercher, filtre)
573     #   dicoValeurs = self.creeDictCondition(self.mcListe, condition=1)
574     #   l=[]
575     #   for k,v in self.jdc.sdsDict.items():
576     #      if (isinstance(v, classeAChercher)) :
577     #         if v.executeExpression(filtre,dicoValeurs) : l.append(k)
578     #   return l
579
580
581
582 def intersection_vide(dict1, dict2):
583     """Verification qu'il n'y a pas de clé commune entre 'dict1' et 'dict2'."""
584     sk1 = set(dict1)
585     sk2 = set(dict2)
586     inter = sk1.intersection(sk2)
587     ok = len(inter) == 0
588     if not ok:
589         print(('ERREUR: Mot(s)-clef(s) vu(s) plusieurs fois :', tuple(inter)))
590     return ok