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