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