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