]> SALOME platform Git repositories - tools/eficas.git/blob - Noyau/N_MCCOMPO.py
Salome HOME
Modifications liees a MT
[tools/eficas.git] / Noyau / N_MCCOMPO.py
1 # coding=utf-8
2 # Copyright (C) 2007-2017   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         #print (self.dicoPyxbDeConstruction)
49         if CONTEXT.debug:
50             print(("MCCOMPO.buildMc ", self.nom))
51         # Dans la phase de reconstruction args peut contenir des mots-clés
52         # qui ne sont pas dans le dictionnaire des entites de definition (self.definition.entites)
53         # de l'objet courant (self)
54         # mais qui sont malgré tout des descendants de l'objet courant
55         # (petits-fils, ...)
56         args = 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         # PN ligne suivante uniquement pour commodite
72         # a detruire quand cela fonctionne recursivement
73         if not hasattr(self, 'dicoPyxbDeConstruction') : self.dicoPyxbDeConstruction = {}
74         for k, v in list(self.definition.entites.items()):
75             if v.label != 'SIMP':
76                 continue
77             if k in args or v.statut == 'o':
78                 #
79                 # Creation par appel de la methode __call__ de la definition de la sous entite k de self
80                 # si une valeur existe dans args ou est obligatoire (generique si toutes les
81                 # entites ont l attribut statut )
82                 #
83                 if self.dicoPyxbDeConstruction and  k in self.dicoPyxbDeConstruction : 
84                    objPyxbDeConstruction=self.dicoPyxbDeConstruction[k]
85                    del self.dicoPyxbDeConstruction[k]
86                 else :
87                    objPyxbDeConstruction=None
88                 objet = v(val=args.get(k, None), nom=k, parent=self,objPyxbDeConstruction=objPyxbDeConstruction)
89                 mcListe.append(objet)
90                 # Si l'objet a une position globale on l'ajoute aux listes
91                 # correspondantes
92                 if hasattr(objet.definition, 'position'):
93                     if objet.definition.position == 'global':
94                         self.append_mc_global(objet)
95                     elif objet.definition.position == 'global_jdc':
96                         self.append_mc_global_jdc(objet)
97             if k in args:
98                 del args[k]
99
100         # Phase 1.2 : on traite les autres entites que SIMP
101         # (FACT en fait car un BLOC ne peut avoir le meme nom qu'un mot-clef)
102         for k, v in list(self.definition.entites.items()):
103             if v.label == 'SIMP':
104                 continue
105             if k in args or v.statut == 'o':
106                 #
107                 # Creation par appel de la methode __call__ de la definition de la sous entite k de self
108                 # si une valeur existe dans args ou est obligatoire (generique si toutes les
109                 # entites ont l attribut statut )
110                 #
111                 if self.dicoPyxbDeConstruction and  k in self.dicoPyxbDeConstruction : 
112                    dicoPyxbDeConstruction=self.dicoPyxbDeConstruction[k]
113                    del self.dicoPyxbDeConstruction[k]
114                 else :
115                    dicoPyxbDeConstruction=None
116                 objet = v(val=args.get(k, None), nom=k, parent=self,dicoPyxbDeConstruction=dicoPyxbDeConstruction)
117                 mcListe.append(objet)
118             if k in args:
119                 del args[k]
120
121         # Phase 2:
122         # On construit les objets (en général, blocs) conditionnés par les mots-clés précédemment créés.
123         # A ce stade, mcListe ne contient que les fils de l'objet courant
124         # args ne contient plus que des mots-clés qui n'ont pas été attribués car ils sont
125         #      à attribuer à des blocs du niveau inférieur ou bien sont des mots-clés erronés
126         for k, v in list(self.definition.entites.items()):
127             if v.label != 'BLOC':
128                 continue
129             # condition and a or b  : Equivalent de l'expression :  condition ?
130             # a : b du langage C
131
132             #PNPN on recalcule dico_valeurs dans le for
133             # pour les globaux imbriques (exple Telemac Advection)
134             # avant le calcul etait avant le for
135             dico_valeurs = self.creeDictCondition(mcListe, condition=1)
136             globs = self.jdc and self.jdc.condition_context or {}
137             if v.verifPresence(dico_valeurs, globs):
138                     # Si le bloc existe :
139                     #        1- on le construit
140                     #        2- on l'ajoute à mcListe
141                     #        3- on récupère les arguments restant
142                     # 4- on reconstruit le dictionnaire équivalent à mcListe
143                 bloc = v(nom=k, val=args, parent=self,dicoPyxbDeConstruction=self.dicoPyxbDeConstruction)
144                 mcListe.append(bloc)
145                 args = bloc.reste_val
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         self.reste_val = args
152         # On ordonne la liste ainsi créée suivant l'ordre du catalogue
153         # (utile seulement pour IHM graphique)
154         mcListe = self.ordonneListe(mcListe)
155         # on retourne la liste ainsi construite
156         if self.jdc  : self.cata=self.jdc.cata
157         else : self.cata = None
158         #self.buildObjPyxb(mcListe)
159         #else : print ('pas de construction pour ', self.nom, self.objPyxbDeConstruction)
160         #print ('buildObjPyxb : ' , self.nom)
161         #print(("MCCOMPO.buildMc fin_____________________________________", self.nom))
162         return mcListe
163
164     def ordonneListe(self, mcListe):
165         """
166            Ordonne la liste suivant l'ordre du catalogue.
167            Seulement pour IHM graphique
168         """
169         if self.jdc and self.jdc.cata_ordonne_dico != None:
170             liste_noms_mc_ordonnee = self.getListeMcOrdonneeBrute(
171                 self.getGenealogie(), self.jdc.cata_ordonne_dico)
172             return self.ordonneListeMc(mcListe, liste_noms_mc_ordonnee)
173         else:
174             return mcListe
175
176     def creeDictValeurs(self, liste=[], condition=0):
177         """
178           Cette méthode crée un contexte (sous la forme d'un dictionnaire)
179           à partir des valeurs des mots clés contenus dans l'argument liste.
180           L'opération consiste à parcourir la liste (d'OBJECT) et à la
181           transformer en un dictionnaire dont les clés sont les noms des
182           mots clés et les valeurs dépendent du type d'OBJECT.
183           Ce dictionnaire servira de liste d'arguments d'appel pour les
184           fonctions sd_prod de commandes et ops de macros ou de contexte
185           d'évaluation des conditions de présence de BLOC.
186
187           Si l'argument condition de la méthode vaut 1, on ne
188           remonte pas les valeurs des mots clés contenus dans des blocs
189           pour eviter les bouclages.
190
191           Cette méthode réalise les opérations suivantes en plus de transformer
192           la liste en dictionnaire :
193
194              - ajouter tous les mots-clés non présents avec la valeur None
195              - ajouter tous les mots-clés globaux (attribut position = 'global'
196                et 'global_jdc')
197
198           L'argument liste est, en général, une mcListe en cours de
199           construction, contenant les mots-clés locaux et les blocs déjà créés.
200
201         """
202         dico = {}
203         for v in liste:
204             if v.isBLOC():
205                 # Si v est un BLOC, on inclut ses items dans le dictionnaire
206                 # représentatif du contexte. Les blocs sont retournés par getValeur
207                 # sous la forme d'un dictionnaire : les mots-clés fils de blocs sont
208                 # donc remontés au niveau du contexte.
209                 if not condition:
210                     dadd = v.getValeur()
211                     assert intersection_vide(dico, dadd)
212                     dico.update(dadd)
213             else:
214                 assert not v.nom in dico, "deja vu : %s" % v.nom
215                 dico[v.nom] = v.getValeur()
216
217         # On rajoute tous les autres mots-clés locaux possibles avec la valeur
218         # par défaut ou None
219         # Pour les mots-clés facteurs, on ne traite que ceux avec statut défaut ('d')
220         # et caché ('c')
221         # On n'ajoute aucune information sur les blocs. Ils n'ont pas de défaut seulement
222         # une condition.
223         # XXX remplacer le not has_key par un dico différent et faire dico2.update(dico)
224         #    ce n'est qu'un pb de perf
225         for k, v in list(self.definition.entites.items()):
226             if not k in  dico :
227                 if v.label == 'SIMP':
228                         # Mot clé simple
229                     dico[k] = v.defaut
230                 elif v.label == 'FACT':
231                     if v.statut in ('c', 'd'):
232                         # Mot clé facteur avec défaut ou caché provisoire
233                         dico[k] = v(val=None, nom=k, parent=self)
234                         # On demande la suppression des pointeurs arrieres
235                         # pour briser les eventuels cycles
236                         dico[k].supprime()
237                     else:
238                         dico[k] = None
239         # A ce stade on a rajouté tous les mots-clés locaux possibles (fils directs) avec leur
240         # valeur par défaut ou la valeur None
241
242         # On rajoute les mots-clés globaux sans écraser les clés existantes
243         dico_mc = self.rechercheMcGlobaux()
244         dico_mc.update(dico)
245         dico = dico_mc
246
247         return dico
248
249     def creeDictToutesValeurs(self):
250         """Semblable à `creeDictValeurs(liste=self.mcListe)` en supprimant les
251         valeurs None."""
252         dico = self.creeDictValeurs(self.mcListe, condition=0)
253         dico = dict([(k, v) for k, v in list(dico.items()) if v is not None])
254         return dico
255
256     def creeDictCondition(self, liste=[], condition=0):
257         """
258             Methode pour construire un contexte qui servira dans l'évaluation
259             des conditions de présence de blocs. Si une commande a un concept
260             produit réutilisé, on ajoute la clé 'reuse'
261         """
262         dico = self.creeDictValeurs(liste, condition=1)
263         # On ajoute la cle "reuse" pour les MCCOMPO qui ont un attribut reuse. A destination
264         # uniquement des commandes. Ne devrait pas etre dans cette classe mais
265         # dans une classe dérivée
266         if not 'reuse' in dico and hasattr(self, 'reuse'):
267             dico['reuse'] = self.reuse
268         return dico
269
270     def rechercheMcGlobaux(self):
271         """
272             Retourne la liste des mots-clés globaux de l'étape à laquelle appartient self
273             et des mots-clés globaux du jdc
274         """
275         etape = self.getEtape()
276         if etape:
277             dict_mc_globaux_fac = self.rechercheMcGlobauxFacultatifs()
278             for k, v in list(etape.mc_globaux.items()):
279                 dict_mc_globaux_fac[k] = v.getValeur()
280             if self.jdc:
281                 for k, v in list(self.jdc.mc_globaux.items()):
282                     dict_mc_globaux_fac[k] = v.getValeur()
283             return dict_mc_globaux_fac
284         else:
285             return {}
286
287     def rechercheMcGlobauxFacultatifs(self):
288         """
289             Cette méthode interroge la définition de self et retourne la liste des mots-clés fils
290             directs de self de type 'global'.
291             position='global' n'est donc possible (et n'a de sens) qu'au plus haut niveau.
292         """
293         dico = {}
294         etape = self.getEtape()
295         if not etape:
296             return {}
297         for k, v in list(etape.definition.entites.items()):
298             if v.label != 'SIMP':
299                 continue
300             if v.position != 'global':
301                 continue
302             if v.statut == 'o':
303                 continue
304             obj = v(val=None, nom=k, parent=etape)
305             dico[k] = obj.getValeur()
306         return dico
307
308     def supprime(self):
309         """
310            Méthode qui supprime toutes les références arrières afin que l'objet puisse
311            etre correctement détruit par le garbage collector
312         """
313         N_OBJECT.OBJECT.supprime(self)
314         for child in self.mcListe:
315             child.supprime()
316
317     def __getitem__(self, key):
318         """
319            Cette méthode retourne la valeur d'un sous mot-clé (key)
320         """
321         return self.getMocle(key)
322
323     def getMocle(self, key):
324         """
325             Retourne la valeur du sous mot-clé key
326             Ce sous mot-clé peut exister, avoir une valeur par defaut ou etre
327             dans un BLOC fils de self
328         """
329         # on cherche dans les mots cles presents, le mot cle de nom key
330         # s'il est là on retourne sa valeur (méthode getVal)
331         for child in self.mcListe:
332             if child.nom == key:
333                 return child.getValeur()
334         #  Si on n a pas trouve de mot cle present on retourne le defaut
335         #  eventuel pour les mots cles accessibles dans la definition
336         #  a ce niveau
337         try:
338             d = self.definition.entites[key]
339             if d.label == 'SIMP':
340                 return d.defaut
341             elif d.label == 'FACT':
342                 # il faut construire les objets necessaires pour
343                 # evaluer les conditions des blocs eventuels (a faire)
344                 if d.statut == 'o':
345                     return None
346                 if d.statut != 'c' and d.statut != 'd':
347                     return None
348                 else:
349                     return d(val=None, nom=key, parent=self)
350         except KeyError:
351             # le mot cle n est pas defini a ce niveau
352             pass
353         #  Si on a toujours rien trouve, on cherche dans les blocs presents
354         #  On suppose que tous les blocs possibles ont ete crees meme ceux
355         #  induits par un mot cle simple absent avec defaut (???)
356         for mc in self.mcListe:
357             if not mc.isBLOC():
358                 continue
359             try:
360                 return mc.getMocle(key)
361             except:
362                 # On n a rien trouve dans ce bloc, on passe au suivant
363                 pass
364         #  On a rien trouve, le mot cle est absent.
365         #  On leve une exception
366         raise IndexError("Le mot cle %s n existe pas dans %s" % (key, self))
367
368     def getChild(self, name, restreint='non'):
369         """
370             Retourne le fils de self de nom name ou None s'il n'existe pas
371             Si restreint vaut oui : ne regarde que dans la mcListe
372             Si restreint vaut non : regarde aussi dans les entites possibles
373             avec defaut (Ce dernier cas n'est utilisé que dans le catalogue)
374         """
375         for v in self.mcListe:
376             if v.nom == name:
377                 return v
378         if restreint == 'non':
379             try:
380                 entite = self.definition.entites[name]
381                 if entite.label == 'SIMP' or (entite.label == 'FACT' and entite.statut in ('c', 'd')):
382                     return entite(None, name, None)
383             except:
384                 pass
385
386         return None
387
388     def append_mc_global(self, mc):
389         """
390            Ajoute le mot-clé mc à la liste des mots-clés globaux de l'étape
391         """
392         etape = self.getEtape()
393         if etape:
394             nom = mc.nom
395             etape.mc_globaux[nom] = mc
396
397     def append_mc_global_jdc(self, mc):
398         """
399             Ajoute le mot-clé mc à la liste des mots-clés globaux du jdc
400         """
401         nom = mc.nom
402         self.jdc.mc_globaux[nom] = mc
403
404     def copy(self):
405         """ Retourne une copie de self """
406         objet = self.makeobjet()
407         # FR : attention !!! avec makeobjet, objet a le meme parent que self
408         # ce qui n'est pas du tout bon dans le cas d'une copie !!!!!!!
409         # FR : peut-on passer par là autrement que dans le cas d'une copie ???
410         # FR --> je suppose que non
411         # XXX CCAR : le pb c'est qu'on vérifie ensuite quel parent avait l'objet
412         # Il me semble preferable de changer le parent a la fin quand la copie
413         # est acceptee
414         objet.valeur = copy(self.valeur)
415         objet.val = copy(self.val)
416         objet.mcListe = []
417         for obj in self.mcListe:
418             new_obj = obj.copy()
419             new_obj.reparent(objet)
420             objet.mcListe.append(new_obj)
421         return objet
422
423     def reparent(self, parent):
424         """
425             Cette methode sert a reinitialiser la parente de l'objet
426         """
427         self.parent = parent
428         self.jdc = parent.getJdcRoot()
429         self.etape = parent.etape
430         for mocle in self.mcListe:
431             mocle.reparent(self)
432
433     def getSd_utilisees(self):
434         """
435           Retourne la liste des concepts qui sont utilisés à l'intérieur de self
436           ( comme valorisation d'un MCS)
437         """
438         l = []
439         for child in self.mcListe:
440             l.extend(child.getSd_utilisees())
441         return l
442
443     def getSd_mcs_utilisees(self):
444         """
445             Retourne la ou les SD utilisée par self sous forme d'un dictionnaire :
446               - Si aucune sd n'est utilisée, le dictionnaire est vide.
447               - Sinon, les clés du dictionnaire sont les mots-clés derrière lesquels on
448                 trouve des sd ; la valeur est la liste des sd attenante.
449                 Exemple ::
450
451                   { 'VALE_F': [ <Cata.cata.fonction_sdaster instance at 0x9419854>,
452                                 <Cata.cata.fonction_sdaster instance at 0x941a204> ],
453                     'MODELE': [<Cata.cata.modele instance at 0x941550c>] }
454         """
455         dico = {}
456         for child in self.mcListe:
457             daux = child.getSd_mcs_utilisees()
458             for cle in daux:
459                 dico[cle] = dico.get(cle, [])
460                 dico[cle].extend(daux[cle])
461         return dico
462
463     def getMcsWithCo(self, co):
464         """
465            Cette methode retourne l'objet MCSIMP fils de self
466            qui a le concept co comme valeur.
467            En principe, elle ne doit etre utilisee que pour les concepts
468            instances de la classe CO
469         """
470         l = []
471         for child in self.mcListe:
472             l.extend(child.getMcsWithCo(co))
473         return l
474
475     def getAllCo(self):
476         """
477            Cette methode retourne tous les concepts instances de CO
478         """
479         l = []
480         for child in self.mcListe:
481             l.extend(child.getAllCo())
482         return l
483
484
485 def intersection_vide(dict1, dict2):
486     """Verification qu'il n'y a pas de clé commune entre 'dict1' et 'dict2'."""
487     sk1 = set(dict1)
488     sk2 = set(dict2)
489     inter = sk1.intersection(sk2)
490     ok = len(inter) == 0
491     if not ok:
492         print(('ERREUR: Mot(s)-clef(s) vu(s) plusieurs fois :', tuple(inter)))
493     return ok