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 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)
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)
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 )
113 if self.dicoPyxbDeConstruction and k in self.dicoPyxbDeConstruction :
114 dicoPyxbDeConstruction=self.dicoPyxbDeConstruction[k]
115 del self.dicoPyxbDeConstruction[k]
117 dicoPyxbDeConstruction=None
118 objet = v(val=args.get(k, None), nom=k, parent=self,dicoPyxbDeConstruction=dicoPyxbDeConstruction)
119 mcListe.append(objet)
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 :
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)
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)
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)
167 def buildMcApresGlobalEnSuppression(self):
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)):
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' :
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()
195 mc.reConstruitResteVal()
198 def buildMcApresGlobalEnCreation(self):
199 nouveau_args = self.reste_val
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)
211 self.mcListe.append(bloc)
212 bloc.addObjPyxb(self.chercheIndiceDsLeContenu(bloc))
213 nouveau_args = self.reste_val
214 self.reste_val = bloc.reste_val
217 def ordonneListe(self, mcListe):
219 Ordonne la liste suivant l'ordre du catalogue.
220 Seulement pour IHM graphique
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)
229 def creeDictValeurs(self, liste=[], condition=0):
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.
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.
244 Cette méthode réalise les opérations suivantes en plus de transformer
245 la liste en dictionnaire :
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'
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.
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.
264 assert intersection_vide(dico, dadd)
267 assert not v.nom in dico, "deja vu : %s" % v.nom
268 dico[v.nom] = v.getValeur()
270 # On rajoute tous les autres mots-clés locaux possibles avec la valeur
272 # Pour les mots-clés facteurs, on ne traite que ceux avec statut défaut ('d')
274 # On n'ajoute aucune information sur les blocs. Ils n'ont pas de défaut seulement
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()):
280 if v.label == 'SIMP':
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
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
295 # On rajoute les mots-clés globaux sans écraser les clés existantes
296 dico_mc = self.rechercheMcGlobaux()
302 def creeDictToutesValeurs(self):
303 """Semblable à `creeDictValeurs(liste=self.mcListe)` en supprimant les
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])
309 def creeDictCondition(self, liste=[], condition=0):
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'
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
323 def rechercheMcGlobaux(self):
325 Retourne la liste des mots-clés globaux de l'étape à laquelle appartient self
326 et des mots-clés globaux du jdc
328 etape = self.getEtape()
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()
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
340 def rechercheMcGlobauxFacultatifs(self):
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
347 #print ('je passe par ici', self.nom)
349 etape = self.getEtape()
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()
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
367 N_OBJECT.OBJECT.supprime(self)
368 for child in self.mcListe:
371 def __getitem__(self, key):
373 Cette méthode retourne la valeur d'un sous mot-clé (key)
375 return self.getMocle(key)
377 def getMocle(self, key):
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
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:
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
392 d = self.definition.entites[key]
393 if d.label == 'SIMP':
395 elif d.label == 'FACT':
396 # il faut construire les objets necessaires pour
397 # evaluer les conditions des blocs eventuels (a faire)
400 if d.statut != 'c' and d.statut != 'd':
403 return d(val=None, nom=key, parent=self)
405 # le mot cle n est pas defini a ce niveau
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:
414 return mc.getMocle(key)
416 # On n a rien trouve dans ce bloc, on passe au suivant
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))
422 def getChild(self, name, restreint='non'):
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)
429 for v in self.mcListe:
432 if restreint == 'non':
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)
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':
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)
455 for v in self.mcListe:
456 if v.nature == 'MCBLOC' :
457 petitFils=v.getChildOrChildInBloc(name, restreint)
458 if petitFils !=None : return petitFils
461 def append_mc_global_avecRecalcule(self, mc):
462 etape = self.getEtape()
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)
471 def append_mc_global(self, mc):
473 Ajoute le mot-clé mc à la liste des mots-clés globaux de l'étape
475 etape = self.getEtape()
478 etape.mc_globaux[nom] = mc
480 def append_mc_global_jdc(self, mc):
482 Ajoute le mot-clé mc à la liste des mots-clés globaux du jdc
485 self.jdc.mc_globaux[nom] = mc
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
495 objet.valeur = copy(self.valeur)
496 objet.val = copy(self.val)
498 for obj in self.mcListe:
500 new_obj.reparent(objet)
501 objet.mcListe.append(new_obj)
504 def reparent(self, parent):
506 Cette methode sert a reinitialiser la parente de l'objet
509 self.jdc = parent.getJdcRoot()
510 self.etape = parent.etape
511 for mocle in self.mcListe:
514 def getSd_utilisees(self):
516 Retourne la liste des concepts qui sont utilisés à l'intérieur de self
517 ( comme valorisation d'un MCS)
520 for child in self.mcListe:
521 l.extend(child.getSd_utilisees())
524 def getSd_mcs_utilisees(self):
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.
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>] }
537 for child in self.mcListe:
538 daux = child.getSd_mcs_utilisees()
540 dico[cle] = dico.get(cle, [])
541 dico[cle].extend(daux[cle])
544 def getMcsWithCo(self, co):
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
552 for child in self.mcListe:
553 l.extend(child.getMcsWithCo(co))
558 Cette methode retourne tous les concepts instances de CO
561 for child in self.mcListe:
562 l.extend(child.getAllCo())
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)
575 # for k,v in self.jdc.sdsDict.items():
576 # if (isinstance(v, classeAChercher)) :
577 # if v.executeExpression(filtre,dicoValeurs) : l.append(k)
582 def intersection_vide(dict1, dict2):
583 """Verification qu'il n'y a pas de clé commune entre 'dict1' et 'dict2'."""
586 inter = sk1.intersection(sk2)
589 print(('ERREUR: Mot(s)-clef(s) vu(s) plusieurs fois :', tuple(inter)))