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