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