3 # GDBus - GLib D-Bus Library
5 # Copyright (C) 2008-2011 Red Hat, Inc.
7 # This library is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU Lesser General Public
9 # License as published by the Free Software Foundation; either
10 # version 2 of the License, or (at your option) any later version.
12 # This library is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 # Lesser General Public License for more details.
17 # You should have received a copy of the GNU Lesser General
18 # Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
20 # Author: David Zeuthen <davidz@redhat.com>
23 import xml.parsers.expat
25 from . import dbustypes
30 STATE_INTERFACE = 'interface'
31 STATE_METHOD = 'method'
32 STATE_SIGNAL = 'signal'
33 STATE_PROPERTY = 'property'
35 STATE_ANNOTATION = 'annotation'
36 STATE_IGNORED = 'ignored'
38 def __init__(self, xml_data):
39 self._parser = xml.parsers.expat.ParserCreate()
40 self._parser.CommentHandler = self.handle_comment
41 self._parser.CharacterDataHandler = self.handle_char_data
42 self._parser.StartElementHandler = self.handle_start_element
43 self._parser.EndElementHandler = self.handle_end_element
45 self.parsed_interfaces = []
46 self._cur_object = None
48 self.state = DBusXMLParser.STATE_TOP
50 self._cur_object = None
51 self._cur_object_stack = []
53 self.doc_comment_last_symbol = ''
55 self._parser.Parse(xml_data)
57 COMMENT_STATE_BEGIN = 'begin'
58 COMMENT_STATE_PARAMS = 'params'
59 COMMENT_STATE_BODY = 'body'
60 COMMENT_STATE_SKIP = 'skip'
61 def handle_comment(self, data):
62 comment_state = DBusXMLParser.COMMENT_STATE_BEGIN;
63 lines = data.split('\n')
71 if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
73 colon_index = line.find(': ')
75 if line.endswith(':'):
76 symbol = line[0:len(line)-1]
77 comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
79 comment_state = DBusXMLParser.COMMENT_STATE_SKIP
81 symbol = line[0:colon_index]
82 rest_of_line = line[colon_index+2:].strip()
83 if len(rest_of_line) > 0:
84 body += '<para>' + rest_of_line + '</para>'
85 comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
86 elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
87 if line.startswith('@'):
88 colon_index = line.find(': ')
90 comment_state = DBusXMLParser.COMMENT_STATE_BODY
94 body += orig_line + '\n'
96 param = line[1:colon_index]
97 docs = line[colon_index + 2:]
100 comment_state = DBusXMLParser.COMMENT_STATE_BODY
105 body += orig_line + '\n'
106 elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
111 body += orig_line + '\n'
120 self.doc_comment_last_symbol = symbol
121 self.doc_comment_params = params
122 self.doc_comment_body = body
124 def handle_char_data(self, data):
125 #print 'char_data=%s'%data
128 def handle_start_element(self, name, attrs):
129 old_state = self.state
130 old_cur_object = self._cur_object
131 if self.state == DBusXMLParser.STATE_IGNORED:
132 self.state = DBusXMLParser.STATE_IGNORED
133 elif self.state == DBusXMLParser.STATE_TOP:
134 if name == DBusXMLParser.STATE_NODE:
135 self.state = DBusXMLParser.STATE_NODE
137 self.state = DBusXMLParser.STATE_IGNORED
138 elif self.state == DBusXMLParser.STATE_NODE:
139 if name == DBusXMLParser.STATE_INTERFACE:
140 self.state = DBusXMLParser.STATE_INTERFACE
141 iface = dbustypes.Interface(attrs['name'])
142 self._cur_object = iface
143 self.parsed_interfaces.append(iface)
144 elif name == DBusXMLParser.STATE_ANNOTATION:
145 self.state = DBusXMLParser.STATE_ANNOTATION
146 anno = dbustypes.Annotation(attrs['name'], attrs['value'])
147 self._cur_object.annotations.append(anno)
148 self._cur_object = anno
150 self.state = DBusXMLParser.STATE_IGNORED
152 # assign docs, if any
153 if 'name' in attrs and self.doc_comment_last_symbol == attrs['name']:
154 self._cur_object.doc_string = self.doc_comment_body
155 if 'short_description' in self.doc_comment_params:
156 short_description = self.doc_comment_params['short_description']
157 self._cur_object.doc_string_brief = short_description
158 if 'since' in self.doc_comment_params:
159 self._cur_object.since = \
160 self.doc_comment_params['since'].strip()
162 elif self.state == DBusXMLParser.STATE_INTERFACE:
163 if name == DBusXMLParser.STATE_METHOD:
164 self.state = DBusXMLParser.STATE_METHOD
165 method = dbustypes.Method(attrs['name'])
166 self._cur_object.methods.append(method)
167 self._cur_object = method
168 elif name == DBusXMLParser.STATE_SIGNAL:
169 self.state = DBusXMLParser.STATE_SIGNAL
170 signal = dbustypes.Signal(attrs['name'])
171 self._cur_object.signals.append(signal)
172 self._cur_object = signal
173 elif name == DBusXMLParser.STATE_PROPERTY:
174 self.state = DBusXMLParser.STATE_PROPERTY
175 prop = dbustypes.Property(attrs['name'], attrs['type'], attrs['access'])
176 self._cur_object.properties.append(prop)
177 self._cur_object = prop
178 elif name == DBusXMLParser.STATE_ANNOTATION:
179 self.state = DBusXMLParser.STATE_ANNOTATION
180 anno = dbustypes.Annotation(attrs['name'], attrs['value'])
181 self._cur_object.annotations.append(anno)
182 self._cur_object = anno
184 self.state = DBusXMLParser.STATE_IGNORED
186 # assign docs, if any
187 if 'name' in attrs and self.doc_comment_last_symbol == attrs['name']:
188 self._cur_object.doc_string = self.doc_comment_body
189 if 'since' in self.doc_comment_params:
190 self._cur_object.since = \
191 self.doc_comment_params['since'].strip()
193 elif self.state == DBusXMLParser.STATE_METHOD:
194 if name == DBusXMLParser.STATE_ARG:
195 self.state = DBusXMLParser.STATE_ARG
198 arg_name = attrs['name']
199 arg = dbustypes.Arg(arg_name, attrs['type'])
200 direction = attrs.get('direction', 'in')
201 if direction == 'in':
202 self._cur_object.in_args.append(arg)
203 elif direction == 'out':
204 self._cur_object.out_args.append(arg)
206 raise RuntimeError('Invalid direction "%s"'%(direction))
207 self._cur_object = arg
208 elif name == DBusXMLParser.STATE_ANNOTATION:
209 self.state = DBusXMLParser.STATE_ANNOTATION
210 anno = dbustypes.Annotation(attrs['name'], attrs['value'])
211 self._cur_object.annotations.append(anno)
212 self._cur_object = anno
214 self.state = DBusXMLParser.STATE_IGNORED
216 # assign docs, if any
217 if self.doc_comment_last_symbol == old_cur_object.name:
218 if 'name' in attrs and attrs['name'] in self.doc_comment_params:
219 doc_string = self.doc_comment_params[attrs['name']]
220 if doc_string != None:
221 self._cur_object.doc_string = doc_string
222 if 'since' in self.doc_comment_params:
223 self._cur_object.since = \
224 self.doc_comment_params['since'].strip()
226 elif self.state == DBusXMLParser.STATE_SIGNAL:
227 if name == DBusXMLParser.STATE_ARG:
228 self.state = DBusXMLParser.STATE_ARG
231 arg_name = attrs['name']
232 arg = dbustypes.Arg(arg_name, attrs['type'])
233 self._cur_object.args.append(arg)
234 self._cur_object = arg
235 elif name == DBusXMLParser.STATE_ANNOTATION:
236 self.state = DBusXMLParser.STATE_ANNOTATION
237 anno = dbustypes.Annotation(attrs['name'], attrs['value'])
238 self._cur_object.annotations.append(anno)
239 self._cur_object = anno
241 self.state = DBusXMLParser.STATE_IGNORED
243 # assign docs, if any
244 if self.doc_comment_last_symbol == old_cur_object.name:
245 if 'name' in attrs and attrs['name'] in self.doc_comment_params:
246 doc_string = self.doc_comment_params[attrs['name']]
247 if doc_string != None:
248 self._cur_object.doc_string = doc_string
249 if 'since' in self.doc_comment_params:
250 self._cur_object.since = \
251 self.doc_comment_params['since'].strip()
253 elif self.state == DBusXMLParser.STATE_PROPERTY:
254 if name == DBusXMLParser.STATE_ANNOTATION:
255 self.state = DBusXMLParser.STATE_ANNOTATION
256 anno = dbustypes.Annotation(attrs['name'], attrs['value'])
257 self._cur_object.annotations.append(anno)
258 self._cur_object = anno
260 self.state = DBusXMLParser.STATE_IGNORED
262 elif self.state == DBusXMLParser.STATE_ARG:
263 if name == DBusXMLParser.STATE_ANNOTATION:
264 self.state = DBusXMLParser.STATE_ANNOTATION
265 anno = dbustypes.Annotation(attrs['name'], attrs['value'])
266 self._cur_object.annotations.append(anno)
267 self._cur_object = anno
269 self.state = DBusXMLParser.STATE_IGNORED
271 elif self.state == DBusXMLParser.STATE_ANNOTATION:
272 if name == DBusXMLParser.STATE_ANNOTATION:
273 self.state = DBusXMLParser.STATE_ANNOTATION
274 anno = dbustypes.Annotation(attrs['name'], attrs['value'])
275 self._cur_object.annotations.append(anno)
276 self._cur_object = anno
278 self.state = DBusXMLParser.STATE_IGNORED
281 raise RuntimeError('Unhandled state "%s" while entering element with name "%s"'%(self.state, name))
283 self.state_stack.append(old_state)
284 self._cur_object_stack.append(old_cur_object)
286 def handle_end_element(self, name):
287 self.state = self.state_stack.pop()
288 self._cur_object = self._cur_object_stack.pop()
290 def parse_dbus_xml(xml_data):
291 parser = DBusXMLParser(xml_data)
292 return parser.parsed_interfaces