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