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