Salome HOME
fin du menage
[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                     elif objet.definition.position == 'global_jdc':
95                         self.append_mc_global_jdc(objet)
96             if k in args:
97                 del args[k]
98
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':
103                 continue
104             if k in args or v.statut == 'o':
105                 #
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 )
109                 #
110                 if self.dicoPyxbDeConstruction and  k in self.dicoPyxbDeConstruction : 
111                    dicoPyxbDeConstruction=self.dicoPyxbDeConstruction[k]
112                    del self.dicoPyxbDeConstruction[k]
113                 else :
114                    dicoPyxbDeConstruction=None
115                 objet = v(val=args.get(k, None), nom=k, parent=self,dicoPyxbDeConstruction=dicoPyxbDeConstruction)
116                 mcListe.append(objet)
117             if k in args:
118                 del args[k]
119
120         # Phase 2:
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 :
134                     #        1- on le construit
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)
139                 mcListe.append(bloc)
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)
144
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))
157         return mcListe
158
159     def buildMcApresGlobal(self):
160         print ('Noyau ---------------- buildMcApresGlobal pour', self.nom)
161         nouveau_args = self.reste_val
162         blocsDejaLa=[]
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)
172                 if bloc :
173                    self.mcListe.append(bloc)
174                    self.reste_val = bloc.reste_val
175        
176
177     def ordonneListe(self, mcListe):
178         """
179            Ordonne la liste suivant l'ordre du catalogue.
180            Seulement pour IHM graphique
181         """
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)
186         else:
187             return mcListe
188
189     def creeDictValeurs(self, liste=[], condition=0):
190         """
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.
199
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.
203
204           Cette méthode réalise les opérations suivantes en plus de transformer
205           la liste en dictionnaire :
206
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'
209                et 'global_jdc')
210
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.
213
214         """
215         dico = {}
216         for v in liste:
217             if v.isBLOC():
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.
222                 if not condition:
223                     dadd = v.getValeur()
224                     assert intersection_vide(dico, dadd)
225                     dico.update(dadd)
226             else:
227                 assert not v.nom in dico, "deja vu : %s" % v.nom
228                 dico[v.nom] = v.getValeur()
229
230         # On rajoute tous les autres mots-clés locaux possibles avec la valeur
231         # par défaut ou None
232         # Pour les mots-clés facteurs, on ne traite que ceux avec statut défaut ('d')
233         # et caché ('c')
234         # On n'ajoute aucune information sur les blocs. Ils n'ont pas de défaut seulement
235         # une condition.
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()):
239             if not k in  dico :
240                 if v.label == 'SIMP':
241                         # Mot clé simple
242                     dico[k] = v.defaut
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
249                         dico[k].supprime()
250                     else:
251                         dico[k] = None
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
254
255         # On rajoute les mots-clés globaux sans écraser les clés existantes
256         dico_mc = self.rechercheMcGlobaux()
257         dico_mc.update(dico)
258         dico = dico_mc
259
260         return dico
261
262     def creeDictToutesValeurs(self):
263         """Semblable à `creeDictValeurs(liste=self.mcListe)` en supprimant les
264         valeurs None."""
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])
267         return dico
268
269     def creeDictCondition(self, liste=[], condition=0):
270         """
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'
274         """
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
281         return dico
282
283     def rechercheMcGlobaux(self):
284         """
285             Retourne la liste des mots-clés globaux de l'étape à laquelle appartient self
286             et des mots-clés globaux du jdc
287         """
288         etape = self.getEtape()
289         if etape:
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()
293             if self.jdc:
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
297         else:
298             return {}
299
300     def rechercheMcGlobauxFacultatifs(self):
301         """
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.
305         """
306         dico = {}
307         etape = self.getEtape()
308         if not etape:
309             return {}
310         for k, v in list(etape.definition.entites.items()):
311             if v.label != 'SIMP':
312                 continue
313             if v.position != 'global':
314                 continue
315             if v.statut == 'o':
316                 continue
317             obj = v(val=None, nom=k, parent=etape)
318             dico[k] = obj.getValeur()
319         return dico
320
321     def supprime(self):
322         """
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
325         """
326         N_OBJECT.OBJECT.supprime(self)
327         for child in self.mcListe:
328             child.supprime()
329
330     def __getitem__(self, key):
331         """
332            Cette méthode retourne la valeur d'un sous mot-clé (key)
333         """
334         return self.getMocle(key)
335
336     def getMocle(self, key):
337         """
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
341         """
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:
345             if child.nom == key:
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
349         #  a ce niveau
350         try:
351             d = self.definition.entites[key]
352             if d.label == 'SIMP':
353                 return d.defaut
354             elif d.label == 'FACT':
355                 # il faut construire les objets necessaires pour
356                 # evaluer les conditions des blocs eventuels (a faire)
357                 if d.statut == 'o':
358                     return None
359                 if d.statut != 'c' and d.statut != 'd':
360                     return None
361                 else:
362                     return d(val=None, nom=key, parent=self)
363         except KeyError:
364             # le mot cle n est pas defini a ce niveau
365             pass
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:
370             if not mc.isBLOC():
371                 continue
372             try:
373                 return mc.getMocle(key)
374             except:
375                 # On n a rien trouve dans ce bloc, on passe au suivant
376                 pass
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))
380
381     def getChild(self, name, restreint='non'):
382         """
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)
387         """
388         for v in self.mcListe:
389             if v.nom == name:
390                 return v
391         if restreint == 'non':
392             try:
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)
396             except:
397                 pass
398
399         return None
400
401     def append_mc_global(self, mc):
402         """
403            Ajoute le mot-clé mc à la liste des mots-clés globaux de l'étape
404         """
405         etape = self.getEtape()
406         if etape:
407             nom = mc.nom
408             etape.mc_globaux[nom] = mc
409
410     def append_mc_global_jdc(self, mc):
411         """
412             Ajoute le mot-clé mc à la liste des mots-clés globaux du jdc
413         """
414         nom = mc.nom
415         self.jdc.mc_globaux[nom] = mc
416
417     def copy(self):
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
426         # est acceptee
427         objet.valeur = copy(self.valeur)
428         objet.val = copy(self.val)
429         objet.mcListe = []
430         for obj in self.mcListe:
431             new_obj = obj.copy()
432             new_obj.reparent(objet)
433             objet.mcListe.append(new_obj)
434         return objet
435
436     def reparent(self, parent):
437         """
438             Cette methode sert a reinitialiser la parente de l'objet
439         """
440         self.parent = parent
441         self.jdc = parent.getJdcRoot()
442         self.etape = parent.etape
443         for mocle in self.mcListe:
444             mocle.reparent(self)
445
446     def getSd_utilisees(self):
447         """
448           Retourne la liste des concepts qui sont utilisés à l'intérieur de self
449           ( comme valorisation d'un MCS)
450         """
451         l = []
452         for child in self.mcListe:
453             l.extend(child.getSd_utilisees())
454         return l
455
456     def getSd_mcs_utilisees(self):
457         """
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.
462                 Exemple ::
463
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>] }
467         """
468         dico = {}
469         for child in self.mcListe:
470             daux = child.getSd_mcs_utilisees()
471             for cle in daux:
472                 dico[cle] = dico.get(cle, [])
473                 dico[cle].extend(daux[cle])
474         return dico
475
476     def getMcsWithCo(self, co):
477         """
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
482         """
483         l = []
484         for child in self.mcListe:
485             l.extend(child.getMcsWithCo(co))
486         return l
487
488     def getAllCo(self):
489         """
490            Cette methode retourne tous les concepts instances de CO
491         """
492         l = []
493         for child in self.mcListe:
494             l.extend(child.getAllCo())
495         return l
496
497
498
499 def intersection_vide(dict1, dict2):
500     """Verification qu'il n'y a pas de clé commune entre 'dict1' et 'dict2'."""
501     sk1 = set(dict1)
502     sk2 = set(dict2)
503     inter = sk1.intersection(sk2)
504     ok = len(inter) == 0
505     if not ok:
506         print(('ERREUR: Mot(s)-clef(s) vu(s) plusieurs fois :', tuple(inter)))
507     return ok