Salome HOME
Join modifications from BR_Dev_For_4_0 tag V4_1_1.
[modules/kernel.git] / src / ModuleGenerator / IDLparser.py
1 #  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
2 #  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
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 #  File   : IDLparser.py
23 #  Module : SALOME
24
25 import string, sys, fpformat, re, os
26 import xml.sax
27 import pdb
28
29 from xml.sax.handler import *
30 from omniidl import idlast, idltype, idlvisitor, idlutil, output
31
32 # parameters not found in IDL file, user's specified in optional parameters
33 common_data={"AUTHOR"     : "",
34              "ICON"       : "",
35              "VERSION"    : "",
36              "COMP_TYPE"  : "",
37              "COMP_NAME"  : "",
38              "COMP_UNAME" : "",
39              "COMP_MULT"  : "",
40              "COMP_IMPL"  : ""
41              }
42
43 nb_components = 0
44
45 #--------------------------------------------------
46 # extract value of <param_name> from <args> list
47 # it's proposed that the matching <args> item
48 # looks like <param_name>=<value>, for example,
49 # catalog=/tmp/myxml.xml
50 #--------------------------------------------------
51 def getParamValue( param_name, default_value, args ):
52     pattern="^"+param_name+"="
53
54     res = default_value        #initial value
55     for opt in args:
56         s = re.compile(pattern).search(opt)
57         if s:
58             res = opt[s.end():]
59             break     #found
60
61     return res    
62
63
64 #--------------------------------------------------
65 # print error message
66 #--------------------------------------------------
67 def error (message):
68     print "ERROR : ", message
69
70
71 #--------------------------------------------------
72 # base class implementing tree
73 #--------------------------------------------------
74 class Tree:
75     
76     def __init__(self, name = '', content = '', key = None):
77         self.name = name
78         self.content = content
79         self.key = key
80         self.parent = None
81         self.childs = []
82         self.comments = []
83         
84     def addChild(self, tree):
85         if tree is not None: 
86             self.childs.append(tree)
87             tree.parent = self
88         return tree
89
90     def addNamedChild(self, name, content = ''):
91         return self.addChild(Tree(name, content))
92
93     def replaceChild(self, tree):
94          if tree is not None:
95             pos = 0
96             for i in self.childs:
97                 if (i.name == tree.name) & ((i.key is None) | (i.key == tree.key)):
98                     self.childs.pop(pos)
99                     self.childs.insert(pos, tree)
100                     return tree
101                 pos += 1
102
103          return self.addChild(tree)
104        
105     def insertFirstChild(self, tree):
106         if tree is not None:
107             self.childs.insert(0, tree)
108         return tree
109     
110     def insertFirstNamedChild(self, name, content = ''):
111         return self.insertFirstChild(Tree(name, content))
112
113     def output_xml(self, f, depth=0):
114         d = depth
115         if self.name != '':
116             s = string.ljust('', 4*depth)
117             s += '<' + self.name + '>'
118             if self.content != '':
119                 s +=  self.content
120             else:
121                 if len(self.childs) > 0:
122                     s += '\n'
123             f.write(s)
124             d +=  1
125             
126         for i in self.childs:
127             i.output_xml(f, d)
128             
129         if self.name != '':
130             s = '</' + self.name + '>\n'
131             if len(self.childs) > 0 :
132                 s = string.ljust('', 4*depth) + s
133             f.write(s)
134
135     def Dump(self, levels=-1, depth=0):
136         #Dumps the tree contents
137         
138         if levels == 0: return
139         
140         s = string.ljust('', 4*depth)
141         print s, self, self.content
142         for i in self.childs:
143             i.Dump(levels-1, depth+1)
144
145     def parents(self):
146         #Returns list of the parents
147         l = []
148         p = self.parent
149         while p:
150             l.append(p)
151             l.append(p.name)
152             p = p.parent
153         return l
154         
155     def getChild(self, name, content=None):
156         # return child node with a given name
157         # if content == None, don't compare contents
158         for i in self.childs:
159             if (i.name == name):
160                 if (content is None) | (i.content == content):
161                     return i
162         return None
163
164     def getNode(self, name, content='', depth=-1):
165         # recursive search of a node with a given name
166         # content == None, don't compare content
167         if (self.name == name):
168             if (content is None) | (self.content == content):
169                 return self
170             
171         if (depth != 0):
172             for i in self.childs:
173                 n = i.getNode(name, content, depth-1)
174                 if n:  return n 
175             
176         return None
177
178     def __repr__(self):
179         s = '<'
180         if self.name != '':
181             s += self.name
182         else:
183             s +=  'None'
184         s += '>'
185         return s
186
187     def merge(self, t):
188         pass
189
190     def mergeChilds(self, t, list):
191         L_ext = t.getChild(list)
192         L_int = self.getChild(list)
193
194         L_merge = Tree(list)
195         
196         for i_ext in L_ext.childs:
197             k_ext = i_ext.key
198             if k_ext is None:  continue
199             present = 0
200             
201             for i_int in L_int.childs:
202                 k_int = i_int.key
203                 if k_int is None:  continue
204                 
205                 if (k_int == k_ext):
206                     present = 1
207                     break;
208                 
209             if present :
210                 i_int.merge(i_ext)
211                 L_merge.addChild(i_int)
212             else:
213                 L_merge.addChild(i_ext)
214                 
215         self.replaceChild(L_merge)
216             
217
218     
219 #--------------------------------------------------
220 # implements parameter tree
221 #--------------------------------------------------
222 class parameter(Tree):
223     
224     def __init__(self, name=None, mode = 'in', type='', comment='unknown'):
225         Tree.__init__(self, mode + 'Parameter', key=name)
226         self.mode = mode
227         if name is None:  return
228         
229         self.addNamedChild(mode + 'Parameter-name', name)
230         self.addNamedChild(mode + 'Parameter-type', type)
231         self.addNamedChild(mode + 'Parameter-comment', comment)
232         
233     def merge(self, P):
234
235         self.mode = P.mode
236         self.replaceChild(P.getChild(P.mode + 'Parameter-name'))
237         self.replaceChild(P.getChild(P.mode + 'Parameter-type'))
238         C = P.getChild(P.mode + 'Parameter-comment')
239         if C.content != 'unkonwn':
240             self.replaceChild(C)
241     
242 #--------------------------------------------------
243 # implements dataStreamParameter tree
244 #--------------------------------------------------
245 class dataStreamParameter(parameter):
246     
247     def __init__(self, name=None, mode='in', type='', dependency='', comment='unknown'):
248         parameter.__init__(self, name, mode, type, comment)
249         if name is None:  return
250         
251         self.addNamedChild(mode + 'Parameter-dependency', dependency)
252         self.mode = mode
253             
254     def merge(self, P):
255
256         parameter.merge(self, P)
257         self.replaceChild(P.getChild(mode + 'Parameter-dependency'))
258
259
260 def parseComment(comment):
261
262     spaces = '[\t\n ]*'
263     word = spaces + '([a-zA-Z][a-zA-Z0-9_]*)' + spaces
264     
265     result = []
266     type = None
267     key = None
268     
269     ## match :  // followed by a 'DataStreamPorts' string,
270     ## the service name, and a list of ports
271     pattern = '// *DataStreamPorts{,1}' + word
272     m = re.match(pattern, comment)
273
274     ## if there is a match, parse remaining part of comment
275     if m:
276         ## service
277         type = 'DataStreamPorts'
278         key = m.group(1)
279         
280         sPorts = comment[m.end():]
281         pattern = word + '\('+word+','+word +','+word+'\)' \
282                   + spaces + ',{,1}' + spaces
283         while len(sPorts) > 0:
284             ## process next DataStreamPort
285             ## match a definition like xx(a,b,c) with a possible trailing ,
286             ## returns a tuple (xx, a, b, c) and
287             ## the remaining part of input string
288             m = re.match(pattern, sPorts)
289             if m is None:
290                 raise LookupError, \
291                       'format error in DataStreamPort definition : '+sPorts
292             sPorts = sPorts[m.end():]
293             result.append(m.groups())
294             
295     return type, key, result;
296
297 #--------------------------------------------------
298 # implements service tree
299 #--------------------------------------------------
300 class Service(Tree):
301     
302     def __init__(self, name=None, comment = 'unknown'):
303         
304         Tree.__init__(self, 'component-service', key=name)
305         if name is None:  return
306         
307         self.addNamedChild('service-name', name)
308         self.addNamedChild('service-author',common_data["AUTHOR"])
309         self.addNamedChild('service-version',common_data["VERSION"])
310         self.addNamedChild('service-comment', comment)
311         self.addNamedChild('service-by-default', "0")
312         self.addNamedChild('inParameter-list')
313         self.addNamedChild('outParameter-list')
314         self.addNamedChild('DataStream-list')
315             
316     def createInParameter(self, name, type):
317         L = self.getChild('inParameter-list')
318         p = parameter(name, 'in', type)
319         L.replaceChild(p)
320         return p
321     
322     def createOutParameter(self, name, type):
323         L = self.getChild('outParameter-list')
324         p = parameter(name, 'out', type)
325         L.replaceChild(p)
326         return p
327
328     def createDataStreamParameter(self, p):
329         L = self.getChild('DataStream-list')
330         p = dataStreamParameter(p[0], p[2], p[1], p[3])
331         L.replaceChild(p)
332         return p
333             
334     def merge(self, S):
335
336         self.replaceChild(S.getChild('service-author'))
337         self.replaceChild(S.getChild('service-version'))
338         self.replaceChild(S.getChild('service-by-default'))
339         C = S.getChild('service-comment')
340         if C.content != 'unkonwn':
341             self.replaceChild(C)
342             
343         for L in ['inParameter-list', 'outParameter-list', 'DataStream-list']:
344            self.mergeChilds(S, L)
345             
346
347
348 #--------------------------------------------------
349 # implements interface tree
350 #--------------------------------------------------
351 class Interface(Tree):
352     
353     def __init__(self, name=None, comment='unknown'):
354                
355         Tree.__init__(self, key=name)
356
357         if name is None:  return
358         
359         self.addNamedChild('component-interface-name', name)
360         self.addNamedChild('component-interface-comment', comment);
361         self.addNamedChild('component-service-list')
362             
363     def createService(self, name):
364         L = self.getChild('component-service-list')
365
366         if L is None:
367             error ("Interface.createService() : 'component-service-list' is not found")
368             return None
369
370         s = Service(name)
371         L.addChild(s)
372         return s
373
374     def findService(self, key):
375         L = self.getChild('component-service-list')
376         for S in L.childs:
377             if S.key == key:
378                 return S
379         return None
380     
381     def merge(self, I):
382
383         C = S.getChild('component-interface-comment')
384         if C.content != 'unkonwn':
385             self.replaceChild(C)
386
387         self.mergeChilds(I, 'component-service-list')
388     
389     def processDataStreams(self):
390         for sComment in self.comments:
391
392             type, key, result = parseComment(sComment)
393
394             if type == 'DataStreamPorts':
395                 Service = self.findService(key)
396                 if Service is None:
397                     raise LookupError, \
398                           'service ' + key + \
399                           ' not found in interface : ' + self.key
400                 for p in result:
401                 ## process next DataStreamPort
402                     Service.createDataStreamParameter(p)
403
404
405 #--------------------------------------------------
406 # implements Component tree
407 #--------------------------------------------------
408 class Component(Tree):
409     def __init__(self, name=None):
410         Tree.__init__(self, 'component', key=name)
411         if name is None:  return
412                  
413 # ASV : fix for bug PAL8922 (Component name indicated by user in GUI is not taken into account
414         if common_data["COMP_NAME"] != '':
415             self.addNamedChild('component-name', common_data["COMP_NAME"])
416         else:
417             self.addNamedChild('component-name', name)
418
419 # ASV : if user name is NOT set, then use component-name instead.  Else - default.
420         if common_data["COMP_UNAME"] != '':
421             self.addNamedChild('component-username',   common_data["COMP_UNAME"])
422         else:
423             if common_data["COMP_NAME"] != '':
424                 self.addNamedChild('component-username', common_data["COMP_NAME"] )
425             else:
426                 self.addNamedChild('component-username',   name)
427             
428         self.addNamedChild('component-type',       common_data["COMP_TYPE"])
429         self.addNamedChild('component-author',     common_data["AUTHOR"])
430         self.addNamedChild('component-version',    common_data["VERSION"])
431         self.addNamedChild('component-comment',    'unknown')
432         self.addNamedChild('component-multistudy', common_data["COMP_MULT"])
433         self.addNamedChild('component-impltype',   common_data["COMP_IMPL"])
434         self.addNamedChild('component-icone',      common_data["ICON"])
435         self.addNamedChild('constraint')
436         self.addNamedChild('component-interface-list')
437             
438     def createInterface(self, name):
439         L = self.getChild('component-interface-list')
440         if L is None:
441             error("createInterface: No component-interface-list is found")
442             return None
443         i = Interface(name)
444         L.addChild(i)
445         return i
446
447     def merge(self, C):
448
449         for i in ['component-username', 'component-author',
450                   'component-type', 'component-icone', 'component-version',
451                   'component-multistudy', 'component-impltype', 'constraint']:
452             ext = C.getChild(i)
453             int = self.getChild(i)
454             if int is None:
455                 int = ext
456             elif ext is not None and len(ext.content):
457                 int.content = ext.content
458                 
459         Cc = C.getChild('component-comment')
460         if Cc.content != 'unkonwn':
461             self.replaceChild(Cc)
462                 
463         self.mergeChilds(C, 'component-interface-list')
464     
465 #--------------------------------------------------
466 # implements document tree
467 #--------------------------------------------------
468 class Catalog(ContentHandler, Tree):
469     def __init__(self, filename = None):
470         Tree.__init__(self)
471         self.buffer = ''
472         self.list = []
473         if (filename):
474             parser = xml.sax.make_parser()
475             parser.setContentHandler(self)
476             parser.parse(filename)
477         else:
478             t = self.addNamedChild('begin-catalog')
479             t.addNamedChild('component-list')
480
481         n = self.getChild('begin-catalog')
482         if n is None:
483             error("Catalog.__init__ : No 'begin-catalog' is found!")
484             return
485         if n.getChild('path-prefix-list') is None:
486             n.insertFirstNamedChild('path-prefix-list')
487         if n.getChild('component-list') is None:
488             n.addNamedChild('component-list')
489             
490     def removeComponent(self, name):
491         complist = self.getNode('component-list')
492         idx = 0
493         if complist is None:
494             print "Catalog.removeComponent() : 'component-list' is not found"
495             return
496         for comp in complist.childs:
497             cname = comp.getChild('component-name')
498             if cname is not None:
499                 if cname.content == name:
500                     complist.childs.pop(idx)
501                     print "Component " + name + " is removed"
502             idx += 1       
503  
504     def startDocument(self):
505         self.list.append(self)
506     
507     def startElement(self, name, attrs):
508         p = self.list[len(self.list)-1]
509         if name == 'component':
510             e = p.addChild(Component())
511         elif name == 'component-interface-name':
512             e = p.addNamedChild(name)
513         elif name == 'component-service':
514             e = p.addChild(Service())
515         elif name == 'inParameter':
516             e = p.addChild(parameter(mode='in'))
517         elif name == 'outParameter':
518             e = p.addChild(parameter(mode='out'))
519         else:
520             e = p.addNamedChild(name)
521         self.list.append(e)
522         self.buffer = ''
523         
524     def endElement(self, name):
525         self.buffer = string.join(string.split(self.buffer), ' ')
526         p = self.list[len(self.list)-1]
527         p.content = self.buffer
528         if name == 'component':
529             p.key = p.getChild('component-name').content
530         self.buffer = ''
531         e = self.list.pop()
532         
533     def characters(self, ch):
534         self.buffer += ch
535
536     def mergeComponent(self, comp):
537         
538         L_int = self.getNode('component-list')
539         if   L_int is None:
540             error("Catalog.mergeComponent : 'component-list' is not found")
541             return
542         
543         i_ext = comp
544         present = 0
545         n_ext = i_ext.key
546         for i_int in L_int.childs:
547             if (i_int.key == n_ext):
548                 present = 1
549                 break;
550                 
551         if present == 0:
552             print '   add component', i_ext.getChild('component-name').content
553             L_int.addChild(i_ext)
554         else:
555             print '   replace component', i_ext.getChild('component-name').content
556             i_int.merge(i_ext)
557             
558
559             
560
561 # IDL file reader
562
563 ttsMap = {
564     idltype.tk_void:       "void",
565     idltype.tk_short:      "short",
566     idltype.tk_long:       "long",
567     idltype.tk_ushort:     "unsigned short",
568     idltype.tk_ulong:      "unsigned long",
569     idltype.tk_float:      "float",
570     idltype.tk_double:     "double",
571     idltype.tk_boolean:    "boolean",
572     idltype.tk_char:       "char",
573     idltype.tk_octet:      "octet",
574     idltype.tk_any:        "any",
575     idltype.tk_TypeCode:   "CORBA::TypeCode",
576     idltype.tk_Principal:  "CORBA::Principal",
577     idltype.tk_longlong:   "long long",
578     idltype.tk_ulonglong:  "unsigned long long",
579     idltype.tk_longdouble: "long double",
580     idltype.tk_wchar:      "wchar"
581     }
582
583
584 #--------------------------------------------------
585 # class ModuleCatalogVisitor
586 #--------------------------------------------------
587 class ModuleCatalogVisitor (idlvisitor.AstVisitor):
588     
589     def __init__(self, catalog):
590         self.catalog = catalog
591         self.EngineType = 0
592         
593     def visitAST(self, node):
594         for n in node.declarations():
595             n.accept(self)
596             
597     def visitModule(self, node):
598         for n in node.definitions():
599             n.accept(self)
600                 
601     def visitInterface(self, node):
602             
603         if node.mainFile():
604
605             self.EngineType = 0
606             
607             for i in node.inherits():
608                 s = i.scopedName();
609                 if ((s[0] == "Engines") & (s[1] == "Component")):
610                     self.EngineType = 1; break
611                 
612             Comp = Component(node.identifier())
613             
614             self.currentInterface = Comp.createInterface(node.identifier())
615         
616             for c in node.callables():
617                 if isinstance(c, idlast.Operation):
618                     c.accept(self)
619
620             for c in node.declarations():
621                 if isinstance(c, idlast.Struct):
622                     c.accept(self)
623                 
624             for i in node.comments():
625                 self.currentInterface.comments.append(str(i))
626
627             self.currentInterface.processDataStreams()
628             
629             if (self.EngineType):    
630                 global nb_components
631                 nb_components = nb_components + 1
632                 self.catalog.mergeComponent(Comp)
633
634             self.EngineType = 0
635             
636
637     def visitOperation(self, node):
638
639         self.currentService = self.currentInterface.createService \
640                                        (node.identifier())
641             
642         node.returnType().accept(self)
643         if (self.currentType != "void"):
644             self.currentService.createOutParameter \
645                 ("return", self.currentType)
646             
647         for c in node.parameters():
648             c.accept(self)
649
650         for i in node.comments():
651             self.currentInterface.comments.append(str(i))
652         
653
654     def visitDeclaredType(self, type):
655         self.currentType = type.name()
656             
657     def visitBaseType(self, type):
658         self.currentType = ttsMap[type.kind()]
659     
660     def visitStringType(self, type):
661         self.currentType = "string"
662         
663     def visitParameter(self, node):
664         node.paramType().accept(self)
665         if node.is_in():
666             self.currentService.createInParameter \
667                      (node.identifier(), self.currentType)
668         if node.is_out():
669             self.currentService.createOutParameter \
670                      (node.identifier(), self.currentType)
671         
672 #--------------------------------------------------
673 # parse idl and store xml file
674 #--------------------------------------------------
675 def run(tree, args):
676     
677     CatalogFileName=getParamValue("catalog", "CatalogModulePersonnel.xml", args)
678     if re.compile(".*?.xml$").match(CatalogFileName, 1) is None:
679         CatalogFileName = CatalogFileName + '.xml'
680
681     #=========  Read parameters  ======================    
682     common_data["ICON"]       = getParamValue("icon",       "",                args)
683     common_data["AUTHOR"]     = getParamValue("author",     os.getenv("USER"), args)
684     common_data["VERSION"]    = getParamValue("version",    "1",               args)
685     common_data["COMP_NAME"]  = getParamValue("name",       "",                args) 
686     common_data["COMP_UNAME"] = getParamValue("username",   "",                args)
687     common_data["COMP_TYPE"]  = getParamValue("type",       "OTHER",           args)
688     common_data["COMP_MULT"]  = getParamValue("multistudy", "1",               args)
689     common_data["COMP_IMPL"]  = getParamValue("impltype",   "1",               args)
690
691     print common_data
692     
693     remove_comp = getParamValue("remove", "", args)
694     
695     #==================================================    
696     
697     if (os.path.exists(CatalogFileName)):
698         print "Importing", CatalogFileName
699         C = Catalog(CatalogFileName)
700     else:
701         print "Creating ",CatalogFileName
702         C = Catalog()
703
704     print "Reading idl file"
705     
706     visitor = ModuleCatalogVisitor(C)
707     tree.accept(visitor)
708
709 ##    C.Dump()
710     
711     if remove_comp :
712         C.removeComponent(remove_comp)
713     
714     if (os.path.exists(CatalogFileName)):
715         print "Updating", CatalogFileName
716         CatalogFileName_old = CatalogFileName + '_old'
717         os.rename(CatalogFileName, CatalogFileName_old)
718     else:
719         CatalogFileName_old = ""
720         print "Writing", CatalogFileName
721         
722     CatalogFileName_new = CatalogFileName + '_new'
723     f=open(CatalogFileName_new, 'w')
724     f.write("<?xml version='1.0' encoding='us-ascii' ?>\n\n")
725     C.output_xml(f)
726     f.close()
727
728     os.rename(CatalogFileName_new, CatalogFileName)
729     if ((CatalogFileName_old != "") & os.path.exists(CatalogFileName_old)):
730         os.unlink(CatalogFileName_old)
731         
732     print
733
734
735 if __name__ == "__main__":
736     print
737     print "Usage : omniidl -bIDLparser [-I<catalog files directory>]* -Wbcatalog=<my_catalog.xml>[,icon=<pngfile>][,version=<num>][,author=<name>][,name=<component_name>][,username=<component_username>][,multistudy=<component_multistudy>][,impltype=<implementation type : 0 (python), 1 (C++)>] <file.idl>"
738     print
739
740