• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# -*- Mode: Python -*-
2
3# GDBus - GLib D-Bus Library
4#
5# Copyright (C) 2008-2011 Red Hat, Inc.
6#
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.1 of the License, or (at your option) any later version.
11#
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.
16#
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/>.
19#
20# Author: David Zeuthen <davidz@redhat.com>
21
22import xml.parsers.expat
23
24from . import dbustypes
25from .utils import print_error
26
27
28class DBusXMLParser:
29    STATE_TOP = "top"
30    STATE_NODE = "node"
31    STATE_INTERFACE = "interface"
32    STATE_METHOD = "method"
33    STATE_SIGNAL = "signal"
34    STATE_PROPERTY = "property"
35    STATE_ARG = "arg"
36    STATE_ANNOTATION = "annotation"
37    STATE_IGNORED = "ignored"
38
39    def __init__(self, xml_data, h_type_implies_unix_fd=True):
40        self._parser = xml.parsers.expat.ParserCreate()
41        self._parser.CommentHandler = self.handle_comment
42        self._parser.CharacterDataHandler = self.handle_char_data
43        self._parser.StartElementHandler = self.handle_start_element
44        self._parser.EndElementHandler = self.handle_end_element
45
46        self.parsed_interfaces = []
47        self._cur_object = None
48
49        self.state = DBusXMLParser.STATE_TOP
50        self.state_stack = []
51        self._cur_object = None
52        self._cur_object_stack = []
53
54        self.doc_comment_last_symbol = ""
55
56        self._h_type_implies_unix_fd = h_type_implies_unix_fd
57
58        self._parser.Parse(xml_data)
59
60    COMMENT_STATE_BEGIN = "begin"
61    COMMENT_STATE_PARAMS = "params"
62    COMMENT_STATE_BODY = "body"
63    COMMENT_STATE_SKIP = "skip"
64
65    def handle_comment(self, data):
66        comment_state = DBusXMLParser.COMMENT_STATE_BEGIN
67        lines = data.split("\n")
68        symbol = ""
69        body = ""
70        in_para = False
71        params = {}
72        for line in lines:
73            orig_line = line
74            line = line.lstrip()
75            if comment_state == DBusXMLParser.COMMENT_STATE_BEGIN:
76                if len(line) > 0:
77                    colon_index = line.find(": ")
78                    if colon_index == -1:
79                        if line.endswith(":"):
80                            symbol = line[0 : len(line) - 1]
81                            comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
82                        else:
83                            comment_state = DBusXMLParser.COMMENT_STATE_SKIP
84                    else:
85                        symbol = line[0:colon_index]
86                        rest_of_line = line[colon_index + 2 :].strip()
87                        if len(rest_of_line) > 0:
88                            body += "<para>" + rest_of_line + "</para>"
89                        comment_state = DBusXMLParser.COMMENT_STATE_PARAMS
90            elif comment_state == DBusXMLParser.COMMENT_STATE_PARAMS:
91                if line.startswith("@"):
92                    colon_index = line.find(": ")
93                    if colon_index == -1:
94                        comment_state = DBusXMLParser.COMMENT_STATE_BODY
95                        if not in_para:
96                            body += "<para>"
97                            in_para = True
98                        body += orig_line + "\n"
99                    else:
100                        param = line[1:colon_index]
101                        docs = line[colon_index + 2 :]
102                        params[param] = docs
103                else:
104                    comment_state = DBusXMLParser.COMMENT_STATE_BODY
105                    if len(line) > 0:
106                        if not in_para:
107                            body += "<para>"
108                            in_para = True
109                        body += orig_line + "\n"
110            elif comment_state == DBusXMLParser.COMMENT_STATE_BODY:
111                if len(line) > 0:
112                    if not in_para:
113                        body += "<para>"
114                        in_para = True
115                    body += orig_line + "\n"
116                else:
117                    if in_para:
118                        body += "</para>"
119                        in_para = False
120        if in_para:
121            body += "</para>"
122
123        if symbol != "":
124            self.doc_comment_last_symbol = symbol
125            self.doc_comment_params = params
126            self.doc_comment_body = body
127
128    def handle_char_data(self, data):
129        # print 'char_data=%s'%data
130        pass
131
132    def handle_start_element(self, name, attrs):
133        old_state = self.state
134        old_cur_object = self._cur_object
135        if self.state == DBusXMLParser.STATE_IGNORED:
136            self.state = DBusXMLParser.STATE_IGNORED
137        elif self.state == DBusXMLParser.STATE_TOP:
138            if name == DBusXMLParser.STATE_NODE:
139                self.state = DBusXMLParser.STATE_NODE
140            else:
141                self.state = DBusXMLParser.STATE_IGNORED
142        elif self.state == DBusXMLParser.STATE_NODE:
143            if name == DBusXMLParser.STATE_INTERFACE:
144                self.state = DBusXMLParser.STATE_INTERFACE
145                iface = dbustypes.Interface(attrs["name"])
146                self._cur_object = iface
147                self.parsed_interfaces.append(iface)
148            elif name == DBusXMLParser.STATE_ANNOTATION:
149                self.state = DBusXMLParser.STATE_ANNOTATION
150                anno = dbustypes.Annotation(attrs["name"], attrs["value"])
151                self._cur_object.annotations.append(anno)
152                self._cur_object = anno
153            else:
154                self.state = DBusXMLParser.STATE_IGNORED
155
156            # assign docs, if any
157            if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
158                self._cur_object.doc_string = self.doc_comment_body
159                if "short_description" in self.doc_comment_params:
160                    short_description = self.doc_comment_params["short_description"]
161                    self._cur_object.doc_string_brief = short_description
162                if "since" in self.doc_comment_params:
163                    self._cur_object.since = self.doc_comment_params["since"].strip()
164
165        elif self.state == DBusXMLParser.STATE_INTERFACE:
166            if name == DBusXMLParser.STATE_METHOD:
167                self.state = DBusXMLParser.STATE_METHOD
168                method = dbustypes.Method(
169                    attrs["name"], h_type_implies_unix_fd=self._h_type_implies_unix_fd
170                )
171                self._cur_object.methods.append(method)
172                self._cur_object = method
173            elif name == DBusXMLParser.STATE_SIGNAL:
174                self.state = DBusXMLParser.STATE_SIGNAL
175                signal = dbustypes.Signal(attrs["name"])
176                self._cur_object.signals.append(signal)
177                self._cur_object = signal
178            elif name == DBusXMLParser.STATE_PROPERTY:
179                self.state = DBusXMLParser.STATE_PROPERTY
180                prop = dbustypes.Property(attrs["name"], attrs["type"], attrs["access"])
181                self._cur_object.properties.append(prop)
182                self._cur_object = prop
183            elif name == DBusXMLParser.STATE_ANNOTATION:
184                self.state = DBusXMLParser.STATE_ANNOTATION
185                anno = dbustypes.Annotation(attrs["name"], attrs["value"])
186                self._cur_object.annotations.append(anno)
187                self._cur_object = anno
188            else:
189                self.state = DBusXMLParser.STATE_IGNORED
190
191            # assign docs, if any
192            if "name" in attrs and self.doc_comment_last_symbol == attrs["name"]:
193                self._cur_object.doc_string = self.doc_comment_body
194                if "since" in self.doc_comment_params:
195                    self._cur_object.since = self.doc_comment_params["since"].strip()
196
197        elif self.state == DBusXMLParser.STATE_METHOD:
198            if name == DBusXMLParser.STATE_ARG:
199                self.state = DBusXMLParser.STATE_ARG
200                arg_name = None
201                if "name" in attrs:
202                    arg_name = attrs["name"]
203                arg = dbustypes.Arg(arg_name, attrs["type"])
204                direction = attrs.get("direction", "in")
205                if direction == "in":
206                    self._cur_object.in_args.append(arg)
207                elif direction == "out":
208                    self._cur_object.out_args.append(arg)
209                else:
210                    print_error('Invalid direction "{}"'.format(direction))
211                self._cur_object = arg
212            elif name == DBusXMLParser.STATE_ANNOTATION:
213                self.state = DBusXMLParser.STATE_ANNOTATION
214                anno = dbustypes.Annotation(attrs["name"], attrs["value"])
215                self._cur_object.annotations.append(anno)
216                self._cur_object = anno
217            else:
218                self.state = DBusXMLParser.STATE_IGNORED
219
220            # assign docs, if any
221            if self.doc_comment_last_symbol == old_cur_object.name:
222                if "name" in attrs and attrs["name"] in self.doc_comment_params:
223                    doc_string = self.doc_comment_params[attrs["name"]]
224                    if doc_string is not None:
225                        self._cur_object.doc_string = doc_string
226                    if "since" in self.doc_comment_params:
227                        self._cur_object.since = self.doc_comment_params[
228                            "since"
229                        ].strip()
230
231        elif self.state == DBusXMLParser.STATE_SIGNAL:
232            if name == DBusXMLParser.STATE_ARG:
233                self.state = DBusXMLParser.STATE_ARG
234                arg_name = None
235                if "name" in attrs:
236                    arg_name = attrs["name"]
237                arg = dbustypes.Arg(arg_name, attrs["type"])
238                self._cur_object.args.append(arg)
239                self._cur_object = arg
240            elif name == DBusXMLParser.STATE_ANNOTATION:
241                self.state = DBusXMLParser.STATE_ANNOTATION
242                anno = dbustypes.Annotation(attrs["name"], attrs["value"])
243                self._cur_object.annotations.append(anno)
244                self._cur_object = anno
245            else:
246                self.state = DBusXMLParser.STATE_IGNORED
247
248            # assign docs, if any
249            if self.doc_comment_last_symbol == old_cur_object.name:
250                if "name" in attrs and attrs["name"] in self.doc_comment_params:
251                    doc_string = self.doc_comment_params[attrs["name"]]
252                    if doc_string is not None:
253                        self._cur_object.doc_string = doc_string
254                    if "since" in self.doc_comment_params:
255                        self._cur_object.since = self.doc_comment_params[
256                            "since"
257                        ].strip()
258
259        elif self.state == DBusXMLParser.STATE_PROPERTY:
260            if name == DBusXMLParser.STATE_ANNOTATION:
261                self.state = DBusXMLParser.STATE_ANNOTATION
262                anno = dbustypes.Annotation(attrs["name"], attrs["value"])
263                self._cur_object.annotations.append(anno)
264                self._cur_object = anno
265            else:
266                self.state = DBusXMLParser.STATE_IGNORED
267
268        elif self.state == DBusXMLParser.STATE_ARG:
269            if name == DBusXMLParser.STATE_ANNOTATION:
270                self.state = DBusXMLParser.STATE_ANNOTATION
271                anno = dbustypes.Annotation(attrs["name"], attrs["value"])
272                self._cur_object.annotations.append(anno)
273                self._cur_object = anno
274            else:
275                self.state = DBusXMLParser.STATE_IGNORED
276
277        elif self.state == DBusXMLParser.STATE_ANNOTATION:
278            if name == DBusXMLParser.STATE_ANNOTATION:
279                self.state = DBusXMLParser.STATE_ANNOTATION
280                anno = dbustypes.Annotation(attrs["name"], attrs["value"])
281                self._cur_object.annotations.append(anno)
282                self._cur_object = anno
283            else:
284                self.state = DBusXMLParser.STATE_IGNORED
285
286        else:
287            print_error(
288                'Unhandled state "{}" while entering element with name "{}"'.format(
289                    self.state, name
290                )
291            )
292
293        self.state_stack.append(old_state)
294        self._cur_object_stack.append(old_cur_object)
295
296    def handle_end_element(self, name):
297        self.state = self.state_stack.pop()
298        self._cur_object = self._cur_object_stack.pop()
299
300
301def parse_dbus_xml(xml_data, h_type_implies_unix_fd):
302    parser = DBusXMLParser(xml_data, h_type_implies_unix_fd)
303    return parser.parsed_interfaces
304