2 # Copyright (C) 2007-2021 EDF R&D
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.
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.
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
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 Ce module contient la classe MCCOMPO qui sert à factoriser les comportements
26 from __future__ import absolute_import
27 from __future__ import print_function
29 from . import N_OBJECT
32 class MCCOMPO(N_OBJECT.OBJECT):
35 Classe support d'un OBJECT composite
41 Construit la liste des sous-entites du MCCOMPO
42 à partir du dictionnaire des arguments (valeur)
46 #traceback.print_stack()
47 #print(("MCCOMPO.buildMc _____________________________________", self.nom))
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
56 #print ('MCCOMPO___________________', self.valeur)
57 if args == None: args = {}
61 # On recopie le dictionnaire des arguments pour protéger l'original des
62 # delete (del args[k])
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
71 if not hasattr(self, 'dicoPyxbDeConstruction') : self.dicoPyxbDeConstruction = {}
72 for k, v in list(self.definition.entites.items()):
75 if k in args or v.statut == 'o':
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 )
81 if self.dicoPyxbDeConstruction and k in self.dicoPyxbDeConstruction :
82 objPyxbDeConstruction=self.dicoPyxbDeConstruction[k]
83 del self.dicoPyxbDeConstruction[k]
85 objPyxbDeConstruction=None
86 #print (args.get(k, None))
87 objet = v(val=args.get(k, None), nom=k, parent=self,objPyxbDeConstruction=objPyxbDeConstruction)
89 # Si l'objet a une position globale on l'ajoute aux listes
91 if hasattr(objet.definition, 'position'):
92 if objet.definition.position == 'global':
93 self.append_mc_global(objet)
94 elif objet.definition.position == 'global_jdc':
95 self.append_mc_global_jdc(objet)
99 # Phase 1.2 : on traite les autres entites que SIMP
100 # (FACT en fait car un BLOC ne peut avoir le meme nom qu'un mot-clef)
101 for k, v in list(self.definition.entites.items()):
102 if v.label == 'SIMP':
104 if k in args or v.statut == 'o':
106 # Creation par appel de la methode __call__ de la definition de la sous entite k de self
107 # si une valeur existe dans args ou est obligatoire (generique si toutes les
108 # entites ont l attribut statut )
110 if self.dicoPyxbDeConstruction and k in self.dicoPyxbDeConstruction :
111 dicoPyxbDeConstruction=self.dicoPyxbDeConstruction[k]
112 del self.dicoPyxbDeConstruction[k]
114 dicoPyxbDeConstruction=None
115 objet = v(val=args.get(k, None), nom=k, parent=self,dicoPyxbDeConstruction=dicoPyxbDeConstruction)
116 mcListe.append(objet)
121 # On construit les objets (en général, blocs) conditionnés par les mots-clés précédemment créés.
122 # A ce stade, mcListe ne contient que les fils de l'objet courant
123 # args ne contient plus que des mots-clés qui n'ont pas été attribués car ils sont
124 # à attribuer à des blocs du niveau inférieur ou bien sont des mots-clés erronés
125 for k, v in list(self.definition.entites.items()):
126 if v.label != 'BLOC': continue
127 #PNPN on recalcule dico_valeurs dans le for
128 # pour les globaux imbriques (exple Telemac Advection)
129 # avant le calcul etait avant le for
130 dico_valeurs = self.creeDictCondition(mcListe, condition=1)
131 globs = self.jdc and self.jdc.condition_context or {}
132 if v.verifPresence(dico_valeurs, globs):
133 # Si le bloc existe :
135 # 2- on l'ajoute à mcListe
136 # 3- on récupère les arguments restant
137 # 4- on reconstruit le dictionnaire équivalent à mcListe
138 bloc = v(nom=k, val=args, parent=self,dicoPyxbDeConstruction=self.dicoPyxbDeConstruction)
140 args = bloc.reste_val
141 # On ne recalcule pas le contexte car on ne tient pas compte des blocs
142 # pour évaluer les conditions de présence des blocs
143 # dico_valeurs = self.creeDictValeurs(mcListe)
145 # On conserve les arguments superflus dans l'attribut reste_val
146 self.reste_val = args
147 # On ordonne la liste ainsi créée suivant l'ordre du catalogue
148 # (utile seulement pour IHM graphique)
149 mcListe = self.ordonneListe(mcListe)
150 # on retourne la liste ainsi construite
151 if self.jdc : self.cata=self.jdc.cata
152 else : self.cata = None
153 self.buildObjPyxb(mcListe)
154 #else : print ('pas de construction pour ', self.nom, self.objPyxbDeConstruction)
155 #print ('buildObjPyxb : ' , self.nom)
156 #print(("MCCOMPO.buildMc fin_____________________________________", self.nom))
159 def buildMcApresGlobal(self):
160 print ('Noyau ---------------- buildMcApresGlobal pour', self.nom)
161 nouveau_args = self.reste_val
163 for mc in self.mcListe :
164 if mc.nature == 'MCBLOC' : blocsDejaLa.append(mc.nom)
165 for k, v in list(self.definition.entites.items()):
166 if v.label != 'BLOC': continue
167 if k in blocsDejaLa : continue
168 dico_valeurs = self.creeDictCondition(self.mcListe, condition=1)
169 globs = self.jdc and self.jdc.condition_context or {}
170 if v.verifPresence(dico_valeurs, globs):
171 bloc = v(nom=k, val=nouveau_args, parent=self,dicoPyxbDeConstruction=self.dicoPyxbDeConstruction)
173 self.mcListe.append(bloc)
174 self.reste_val = bloc.reste_val
177 def ordonneListe(self, mcListe):
179 Ordonne la liste suivant l'ordre du catalogue.
180 Seulement pour IHM graphique
182 if self.jdc and self.jdc.cata_ordonne_dico != None:
183 liste_noms_mc_ordonnee = self.getListeMcOrdonneeBrute(
184 self.getGenealogie(), self.jdc.cata_ordonne_dico)
185 return self.ordonneListeMc(mcListe, liste_noms_mc_ordonnee)
189 def creeDictValeurs(self, liste=[], condition=0):
191 Cette méthode crée un contexte (sous la forme d'un dictionnaire)
192 à partir des valeurs des mots clés contenus dans l'argument liste.
193 L'opération consiste à parcourir la liste (d'OBJECT) et à la
194 transformer en un dictionnaire dont les clés sont les noms des
195 mots clés et les valeurs dépendent du type d'OBJECT.
196 Ce dictionnaire servira de liste d'arguments d'appel pour les
197 fonctions sd_prod de commandes et ops de macros ou de contexte
198 d'évaluation des conditions de présence de BLOC.
200 Si l'argument condition de la méthode vaut 1, on ne
201 remonte pas les valeurs des mots clés contenus dans des blocs
202 pour eviter les bouclages.
204 Cette méthode réalise les opérations suivantes en plus de transformer
205 la liste en dictionnaire :
207 - ajouter tous les mots-clés non présents avec la valeur None
208 - ajouter tous les mots-clés globaux (attribut position = 'global'
211 L'argument liste est, en général, une mcListe en cours de
212 construction, contenant les mots-clés locaux et les blocs déjà créés.
218 # Si v est un BLOC, on inclut ses items dans le dictionnaire
219 # représentatif du contexte. Les blocs sont retournés par getValeur
220 # sous la forme d'un dictionnaire : les mots-clés fils de blocs sont
221 # donc remontés au niveau du contexte.
224 assert intersection_vide(dico, dadd)
227 assert not v.nom in dico, "deja vu : %s" % v.nom
228 dico[v.nom] = v.getValeur()
230 # On rajoute tous les autres mots-clés locaux possibles avec la valeur
232 # Pour les mots-clés facteurs, on ne traite que ceux avec statut défaut ('d')
234 # On n'ajoute aucune information sur les blocs. Ils n'ont pas de défaut seulement
236 # XXX remplacer le not has_key par un dico différent et faire dico2.update(dico)
237 # ce n'est qu'un pb de perf
238 for k, v in list(self.definition.entites.items()):
240 if v.label == 'SIMP':
243 elif v.label == 'FACT':
244 if v.statut in ('c', 'd'):
245 # Mot clé facteur avec défaut ou caché provisoire
246 dico[k] = v(val=None, nom=k, parent=self)
247 # On demande la suppression des pointeurs arrieres
248 # pour briser les eventuels cycles
252 # A ce stade on a rajouté tous les mots-clés locaux possibles (fils directs) avec leur
253 # valeur par défaut ou la valeur None
255 # On rajoute les mots-clés globaux sans écraser les clés existantes
256 dico_mc = self.rechercheMcGlobaux()
262 def creeDictToutesValeurs(self):
263 """Semblable à `creeDictValeurs(liste=self.mcListe)` en supprimant les
265 dico = self.creeDictValeurs(self.mcListe, condition=0)
266 dico = dict([(k, v) for k, v in list(dico.items()) if v is not None])
269 def creeDictCondition(self, liste=[], condition=0):
271 Methode pour construire un contexte qui servira dans l'évaluation
272 des conditions de présence de blocs. Si une commande a un concept
273 produit réutilisé, on ajoute la clé 'reuse'
275 dico = self.creeDictValeurs(liste, condition=1)
276 # On ajoute la cle "reuse" pour les MCCOMPO qui ont un attribut reuse. A destination
277 # uniquement des commandes. Ne devrait pas etre dans cette classe mais
278 # dans une classe dérivée
279 if not 'reuse' in dico and hasattr(self, 'reuse'):
280 dico['reuse'] = self.reuse
283 def rechercheMcGlobaux(self):
285 Retourne la liste des mots-clés globaux de l'étape à laquelle appartient self
286 et des mots-clés globaux du jdc
288 etape = self.getEtape()
290 dict_mc_globaux_fac = self.rechercheMcGlobauxFacultatifs()
291 for k, v in list(etape.mc_globaux.items()):
292 dict_mc_globaux_fac[k] = v.getValeur()
294 for k, v in list(self.jdc.mc_globaux.items()):
295 dict_mc_globaux_fac[k] = v.getValeur()
296 return dict_mc_globaux_fac
300 def rechercheMcGlobauxFacultatifs(self):
302 Cette méthode interroge la définition de self et retourne la liste des mots-clés fils
303 directs de self de type 'global'.
304 position='global' n'est donc possible (et n'a de sens) qu'au plus haut niveau.
307 etape = self.getEtape()
310 for k, v in list(etape.definition.entites.items()):
311 if v.label != 'SIMP':
313 if v.position != 'global':
317 obj = v(val=None, nom=k, parent=etape)
318 dico[k] = obj.getValeur()
323 Méthode qui supprime toutes les références arrières afin que l'objet puisse
324 etre correctement détruit par le garbage collector
326 N_OBJECT.OBJECT.supprime(self)
327 for child in self.mcListe:
330 def __getitem__(self, key):
332 Cette méthode retourne la valeur d'un sous mot-clé (key)
334 return self.getMocle(key)
336 def getMocle(self, key):
338 Retourne la valeur du sous mot-clé key
339 Ce sous mot-clé peut exister, avoir une valeur par defaut ou etre
340 dans un BLOC fils de self
342 # on cherche dans les mots cles presents, le mot cle de nom key
343 # s'il est là on retourne sa valeur (méthode getVal)
344 for child in self.mcListe:
346 return child.getValeur()
347 # Si on n a pas trouve de mot cle present on retourne le defaut
348 # eventuel pour les mots cles accessibles dans la definition
351 d = self.definition.entites[key]
352 if d.label == 'SIMP':
354 elif d.label == 'FACT':
355 # il faut construire les objets necessaires pour
356 # evaluer les conditions des blocs eventuels (a faire)
359 if d.statut != 'c' and d.statut != 'd':
362 return d(val=None, nom=key, parent=self)
364 # le mot cle n est pas defini a ce niveau
366 # Si on a toujours rien trouve, on cherche dans les blocs presents
367 # On suppose que tous les blocs possibles ont ete crees meme ceux
368 # induits par un mot cle simple absent avec defaut (???)
369 for mc in self.mcListe:
373 return mc.getMocle(key)
375 # On n a rien trouve dans ce bloc, on passe au suivant
377 # On a rien trouve, le mot cle est absent.
378 # On leve une exception
379 raise IndexError("Le mot cle %s n existe pas dans %s" % (key, self))
381 def getChild(self, name, restreint='non'):
383 Retourne le fils de self de nom name ou None s'il n'existe pas
384 Si restreint vaut oui : ne regarde que dans la mcListe
385 Si restreint vaut non : regarde aussi dans les entites possibles
386 avec defaut (Ce dernier cas n'est utilisé que dans le catalogue)
388 for v in self.mcListe:
391 if restreint == 'non':
393 entite = self.definition.entites[name]
394 if entite.label == 'SIMP' or (entite.label == 'FACT' and entite.statut in ('c', 'd')):
395 return entite(None, name, None)
401 def append_mc_global(self, mc):
403 Ajoute le mot-clé mc à la liste des mots-clés globaux de l'étape
405 etape = self.getEtape()
408 etape.mc_globaux[nom] = mc
410 def append_mc_global_jdc(self, mc):
412 Ajoute le mot-clé mc à la liste des mots-clés globaux du jdc
415 self.jdc.mc_globaux[nom] = mc
418 """ Retourne une copie de self """
419 objet = self.makeobjet()
420 # FR : attention !!! avec makeobjet, objet a le meme parent que self
421 # ce qui n'est pas du tout bon dans le cas d'une copie !!!!!!!
422 # FR : peut-on passer par là autrement que dans le cas d'une copie ???
423 # FR --> je suppose que non
424 # XXX CCAR : le pb c'est qu'on vérifie ensuite quel parent avait l'objet
425 # Il me semble preferable de changer le parent a la fin quand la copie
427 objet.valeur = copy(self.valeur)
428 objet.val = copy(self.val)
430 for obj in self.mcListe:
432 new_obj.reparent(objet)
433 objet.mcListe.append(new_obj)
436 def reparent(self, parent):
438 Cette methode sert a reinitialiser la parente de l'objet
441 self.jdc = parent.getJdcRoot()
442 self.etape = parent.etape
443 for mocle in self.mcListe:
446 def getSd_utilisees(self):
448 Retourne la liste des concepts qui sont utilisés à l'intérieur de self
449 ( comme valorisation d'un MCS)
452 for child in self.mcListe:
453 l.extend(child.getSd_utilisees())
456 def getSd_mcs_utilisees(self):
458 Retourne la ou les SD utilisée par self sous forme d'un dictionnaire :
459 - Si aucune sd n'est utilisée, le dictionnaire est vide.
460 - Sinon, les clés du dictionnaire sont les mots-clés derrière lesquels on
461 trouve des sd ; la valeur est la liste des sd attenante.
464 { 'VALE_F': [ <Cata.cata.fonction_sdaster instance at 0x9419854>,
465 <Cata.cata.fonction_sdaster instance at 0x941a204> ],
466 'MODELE': [<Cata.cata.modele instance at 0x941550c>] }
469 for child in self.mcListe:
470 daux = child.getSd_mcs_utilisees()
472 dico[cle] = dico.get(cle, [])
473 dico[cle].extend(daux[cle])
476 def getMcsWithCo(self, co):
478 Cette methode retourne l'objet MCSIMP fils de self
479 qui a le concept co comme valeur.
480 En principe, elle ne doit etre utilisee que pour les concepts
481 instances de la classe CO
484 for child in self.mcListe:
485 l.extend(child.getMcsWithCo(co))
490 Cette methode retourne tous les concepts instances de CO
493 for child in self.mcListe:
494 l.extend(child.getAllCo())
499 def intersection_vide(dict1, dict2):
500 """Verification qu'il n'y a pas de clé commune entre 'dict1' et 'dict2'."""
503 inter = sk1.intersection(sk2)
506 print(('ERREUR: Mot(s)-clef(s) vu(s) plusieurs fois :', tuple(inter)))