• 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 re
23from os import path
24
25from . import utils
26
27
28# Disable line length warnings as wrapping the Docbook templates would be hard
29# flake8: noqa: E501
30
31
32# ----------------------------------------------------------------------------------------------------
33
34
35class DocbookCodeGenerator:
36    def __init__(self, ifaces):
37        self.ifaces = ifaces
38        self.generate_expand_dicts()
39
40    def print_method_prototype(self, i, m, in_synopsis):
41        max_method_len = 0
42        if in_synopsis:
43            for _m in i.methods:
44                max_method_len = max(len(_m.name), max_method_len)
45        else:
46            max_method_len = max(len(m.name), max_method_len)
47
48        max_signature_len = 0
49        if in_synopsis:
50            for _m in i.methods:
51                for a in _m.in_args:
52                    max_signature_len = max(len(a.signature), max_signature_len)
53                for a in _m.out_args:
54                    max_signature_len = max(len(a.signature), max_signature_len)
55        else:
56            for a in m.in_args:
57                max_signature_len = max(len(a.signature), max_signature_len)
58            for a in m.out_args:
59                max_signature_len = max(len(a.signature), max_signature_len)
60
61        if in_synopsis:
62            self.out.write(
63                '<link linkend="gdbus-method-%s.%s">%s</link>%*s ('
64                % (
65                    utils.dots_to_hyphens(i.name),
66                    m.name,
67                    m.name,
68                    max_method_len - len(m.name),
69                    "",
70                )
71            )
72        else:
73            self.out.write("%s%*s (" % (m.name, max_method_len - len(m.name), ""))
74        count = 0
75        for a in m.in_args:
76            if count > 0:
77                self.out.write(",\n%*s" % (max_method_len + 2, ""))
78            self.out.write(
79                "IN  %s%*s %s"
80                % (a.signature, max_signature_len - len(a.signature), "", a.name)
81            )
82            count = count + 1
83        for a in m.out_args:
84            if count > 0:
85                self.out.write(",\n%*s" % (max_method_len + 2, ""))
86            self.out.write(
87                "OUT %s%*s %s"
88                % (a.signature, max_signature_len - len(a.signature), "", a.name)
89            )
90            count = count + 1
91        self.out.write(");\n")
92
93    def print_signal_prototype(self, i, s, in_synopsis):
94        max_signal_len = 0
95        if in_synopsis:
96            for _s in i.signals:
97                max_signal_len = max(len(_s.name), max_signal_len)
98        else:
99            max_signal_len = max(len(s.name), max_signal_len)
100
101        max_signature_len = 0
102        if in_synopsis:
103            for _s in i.signals:
104                for a in _s.args:
105                    max_signature_len = max(len(a.signature), max_signature_len)
106        else:
107            for a in s.args:
108                max_signature_len = max(len(a.signature), max_signature_len)
109
110        if in_synopsis:
111            self.out.write(
112                '<link linkend="gdbus-signal-%s.%s">%s</link>%*s ('
113                % (
114                    utils.dots_to_hyphens(i.name),
115                    s.name,
116                    s.name,
117                    max_signal_len - len(s.name),
118                    "",
119                )
120            )
121        else:
122            self.out.write("%s%*s (" % (s.name, max_signal_len - len(s.name), ""))
123        count = 0
124        for a in s.args:
125            if count > 0:
126                self.out.write(",\n%*s" % (max_signal_len + 2, ""))
127            self.out.write(
128                "%s%*s %s"
129                % (a.signature, max_signature_len - len(a.signature), "", a.name)
130            )
131            count = count + 1
132        self.out.write(");\n")
133
134    def print_property_prototype(self, i, p, in_synopsis):
135        max_property_len = 0
136        if in_synopsis:
137            for _p in i.properties:
138                max_property_len = max(len(_p.name), max_property_len)
139        else:
140            max_property_len = max(len(p.name), max_property_len)
141
142        max_signature_len = 0
143        if in_synopsis:
144            for _p in i.properties:
145                max_signature_len = max(len(_p.signature), max_signature_len)
146        else:
147            max_signature_len = max(len(p.signature), max_signature_len)
148
149        if in_synopsis:
150            self.out.write(
151                '<link linkend="gdbus-property-%s.%s">%s</link>%*s'
152                % (
153                    utils.dots_to_hyphens(i.name),
154                    p.name,
155                    p.name,
156                    max_property_len - len(p.name),
157                    "",
158                )
159            )
160        else:
161            self.out.write("%s%*s" % (p.name, max_property_len - len(p.name), ""))
162        if p.readable and p.writable:
163            access = "readwrite"
164        elif p.readable:
165            access = "readable "
166        else:
167            access = "writable "
168        self.out.write("  %s  %s\n" % (access, p.signature))
169
170    def print_synopsis_methods(self, i):
171        self.out.write('  <refsynopsisdiv role="synopsis">\n')
172        self.out.write('    <title role="synopsis.title">Methods</title>\n')
173        self.out.write("    <synopsis>\n")
174        for m in i.methods:
175            self.print_method_prototype(i, m, in_synopsis=True)
176        self.out.write("</synopsis>\n")
177        self.out.write("  </refsynopsisdiv>\n")
178
179    def print_synopsis_signals(self, i):
180        self.out.write('  <refsect1 role="signal_proto">\n')
181        self.out.write('    <title role="signal_proto.title">Signals</title>\n')
182        self.out.write("    <synopsis>\n")
183        for s in i.signals:
184            self.print_signal_prototype(i, s, in_synopsis=True)
185        self.out.write("</synopsis>\n")
186        self.out.write("  </refsect1>\n")
187
188    def print_synopsis_properties(self, i):
189        self.out.write('  <refsect1 role="properties">\n')
190        self.out.write('    <title role="properties.title">Properties</title>\n')
191        self.out.write("    <synopsis>\n")
192        for p in i.properties:
193            self.print_property_prototype(i, p, in_synopsis=True)
194        self.out.write("</synopsis>\n")
195        self.out.write("  </refsect1>\n")
196
197    def print_method(self, i, m):
198        self.out.write(
199            '<refsect2 role="method" id="gdbus-method-%s.%s">\n'
200            % (utils.dots_to_hyphens(i.name), m.name)
201        )
202        self.out.write("  <title>The %s() method</title>\n" % (m.name))
203        self.out.write(
204            '  <indexterm zone="gdbus-method-%s.%s"><primary sortas="%s.%s">%s.%s()</primary></indexterm>\n'
205            % (
206                utils.dots_to_hyphens(i.name),
207                m.name,
208                i.name_without_prefix,
209                m.name,
210                i.name,
211                m.name,
212            )
213        )
214        self.out.write("<programlisting>\n")
215        self.print_method_prototype(i, m, in_synopsis=False)
216        self.out.write("</programlisting>\n")
217        self.out.write("%s\n" % (self.expand_paras(m.doc_string, True)))
218        if m.in_args or m.out_args:
219            self.out.write('<variablelist role="params">\n')
220            for a in m.in_args:
221                self.out.write("<varlistentry>\n")
222                self.out.write(
223                    "  <term><literal>IN %s <parameter>%s</parameter></literal>:</term>\n"
224                    % (a.signature, a.name)
225                )
226                self.out.write(
227                    "  <listitem>%s</listitem>\n"
228                    % (self.expand_paras(a.doc_string, True))
229                )
230                self.out.write("</varlistentry>\n")
231            for a in m.out_args:
232                self.out.write("<varlistentry>\n")
233                self.out.write(
234                    "  <term><literal>OUT %s <parameter>%s</parameter></literal>:</term>\n"
235                    % (a.signature, a.name)
236                )
237                self.out.write(
238                    "  <listitem>%s</listitem>\n"
239                    % (self.expand_paras(a.doc_string, True))
240                )
241                self.out.write("</varlistentry>\n")
242            self.out.write("</variablelist>\n")
243        if len(m.since) > 0:
244            self.out.write('<para role="since">Since %s</para>\n' % (m.since))
245        if m.deprecated:
246            self.out.write(
247                "<warning><para>The %s() method is deprecated.</para></warning>"
248                % (m.name)
249            )
250        self.out.write("</refsect2>\n")
251
252    def print_signal(self, i, s):
253        self.out.write(
254            '<refsect2 role="signal" id="gdbus-signal-%s.%s">\n'
255            % (utils.dots_to_hyphens(i.name), s.name)
256        )
257        self.out.write('  <title>The "%s" signal</title>\n' % (s.name))
258        self.out.write(
259            '  <indexterm zone="gdbus-signal-%s.%s"><primary sortas="%s::%s">%s::%s</primary></indexterm>\n'
260            % (
261                utils.dots_to_hyphens(i.name),
262                s.name,
263                i.name_without_prefix,
264                s.name,
265                i.name,
266                s.name,
267            )
268        )
269        self.out.write("<programlisting>\n")
270        self.print_signal_prototype(i, s, in_synopsis=False)
271        self.out.write("</programlisting>\n")
272        self.out.write("%s\n" % (self.expand_paras(s.doc_string, True)))
273        if s.args:
274            self.out.write('<variablelist role="params">\n')
275            for a in s.args:
276                self.out.write("<varlistentry>\n")
277                self.out.write(
278                    "  <term><literal>%s <parameter>%s</parameter></literal>:</term>\n"
279                    % (a.signature, a.name)
280                )
281                self.out.write(
282                    "  <listitem>%s</listitem>\n"
283                    % (self.expand_paras(a.doc_string, True))
284                )
285                self.out.write("</varlistentry>\n")
286            self.out.write("</variablelist>\n")
287        if len(s.since) > 0:
288            self.out.write('<para role="since">Since %s</para>\n' % (s.since))
289        if s.deprecated:
290            self.out.write(
291                '<warning><para>The "%s" signal is deprecated.</para></warning>'
292                % (s.name)
293            )
294        self.out.write("</refsect2>\n")
295
296    def print_property(self, i, p):
297        self.out.write(
298            '<refsect2 role="property" id="gdbus-property-%s.%s">\n'
299            % (utils.dots_to_hyphens(i.name), p.name)
300        )
301        self.out.write('  <title>The "%s" property</title>\n' % (p.name))
302        self.out.write(
303            '  <indexterm zone="gdbus-property-%s.%s"><primary sortas="%s:%s">%s:%s</primary></indexterm>\n'
304            % (
305                utils.dots_to_hyphens(i.name),
306                p.name,
307                i.name_without_prefix,
308                p.name,
309                i.name,
310                p.name,
311            )
312        )
313        self.out.write("<programlisting>\n")
314        self.print_property_prototype(i, p, in_synopsis=False)
315        self.out.write("</programlisting>\n")
316        self.out.write("%s\n" % (self.expand_paras(p.doc_string, True)))
317        if len(p.since) > 0:
318            self.out.write('<para role="since">Since %s</para>\n' % (p.since))
319        if p.deprecated:
320            self.out.write(
321                '<warning><para>The "%s" property is deprecated.</para></warning>'
322                % (p.name)
323            )
324        self.out.write("</refsect2>\n")
325
326    def expand(self, s, expandParamsAndConstants):
327        for key in self.expand_member_dict_keys:
328            s = s.replace(key, self.expand_member_dict[key])
329        for key in self.expand_iface_dict_keys:
330            s = s.replace(key, self.expand_iface_dict[key])
331        if expandParamsAndConstants:
332            # replace @foo with <parameter>foo</parameter>
333            s = re.sub(
334                "@[a-zA-Z0-9_]*",
335                lambda m: "<parameter>" + m.group(0)[1:] + "</parameter>",
336                s,
337            )
338            # replace e.g. %TRUE with <constant>TRUE</constant>
339            s = re.sub(
340                "%[a-zA-Z0-9_]*",
341                lambda m: "<constant>" + m.group(0)[1:] + "</constant>",
342                s,
343            )
344        return s
345
346    def expand_paras(self, s, expandParamsAndConstants):
347        s = self.expand(s, expandParamsAndConstants).strip()
348        if not s.startswith("<para"):
349            s = "<para>%s</para>" % s
350        return s
351
352    def generate_expand_dicts(self):
353        self.expand_member_dict = {}
354        self.expand_iface_dict = {}
355        for i in self.ifaces:
356            key = "#%s" % (i.name)
357            value = '<link linkend="gdbus-interface-%s.top_of_page">%s</link>' % (
358                utils.dots_to_hyphens(i.name),
359                i.name,
360            )
361            self.expand_iface_dict[key] = value
362            for m in i.methods:
363                key = "%s.%s()" % (i.name, m.name)
364                value = '<link linkend="gdbus-method-%s.%s">%s()</link>' % (
365                    utils.dots_to_hyphens(i.name),
366                    m.name,
367                    m.name,
368                )
369                self.expand_member_dict[key] = value
370            for s in i.signals:
371                key = "#%s::%s" % (i.name, s.name)
372                value = '<link linkend="gdbus-signal-%s.%s">"%s"</link>' % (
373                    utils.dots_to_hyphens(i.name),
374                    s.name,
375                    s.name,
376                )
377                self.expand_member_dict[key] = value
378            for p in i.properties:
379                key = "#%s:%s" % (i.name, p.name)
380                value = '<link linkend="gdbus-property-%s.%s">"%s"</link>' % (
381                    utils.dots_to_hyphens(i.name),
382                    p.name,
383                    p.name,
384                )
385                self.expand_member_dict[key] = value
386        # Make sure to expand the keys in reverse order so e.g. #org.foo.Iface:MediaCompat
387        # is evaluated before #org.foo.Iface:Media ...
388        self.expand_member_dict_keys = sorted(
389            self.expand_member_dict.keys(), reverse=True
390        )
391        self.expand_iface_dict_keys = sorted(
392            self.expand_iface_dict.keys(), reverse=True
393        )
394
395    def generate(self, docbook, outdir):
396        for i in self.ifaces:
397            self.out = open(path.join(outdir, "%s-%s.xml" % (docbook, i.name)), "w")
398            self.out.write("")
399            self.out.write('<?xml version="1.0" encoding="utf-8"?>\n')
400            self.out.write(
401                '<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"\n'
402            )
403            self.out.write(
404                '               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [\n'
405            )
406            self.out.write("]>\n")
407            self.out.write('<refentry id="gdbus-%s">\n' % (i.name))
408            self.out.write("  <refmeta>")
409            self.out.write(
410                '    <refentrytitle role="top_of_page" id="gdbus-interface-%s.top_of_page">%s</refentrytitle>\n'
411                % (utils.dots_to_hyphens(i.name), i.name)
412            )
413            self.out.write(
414                '  <indexterm zone="gdbus-interface-%s.top_of_page"><primary sortas="%s">%s</primary></indexterm>\n'
415                % (utils.dots_to_hyphens(i.name), i.name_without_prefix, i.name)
416            )
417            self.out.write("  </refmeta>")
418
419            self.out.write("  <refnamediv>")
420            self.out.write("    <refname>%s</refname>" % (i.name))
421            self.out.write("    <refpurpose>%s</refpurpose>" % (i.doc_string_brief))
422            self.out.write("  </refnamediv>")
423
424            if len(i.methods) > 0:
425                self.print_synopsis_methods(i)
426            if len(i.signals) > 0:
427                self.print_synopsis_signals(i)
428            if len(i.properties) > 0:
429                self.print_synopsis_properties(i)
430
431            self.out.write(
432                '<refsect1 role="desc" id="gdbus-interface-%s">\n'
433                % (utils.dots_to_hyphens(i.name))
434            )
435            self.out.write('  <title role="desc.title">Description</title>\n')
436            self.out.write("  %s\n" % (self.expand_paras(i.doc_string, True)))
437            if len(i.since) > 0:
438                self.out.write('  <para role="since">Since %s</para>\n' % (i.since))
439            if i.deprecated:
440                self.out.write(
441                    "<warning><para>The %s interface is deprecated.</para></warning>"
442                    % (i.name)
443                )
444            self.out.write("</refsect1>\n")
445
446            if len(i.methods) > 0:
447                self.out.write(
448                    '<refsect1 role="details" id="gdbus-methods-%s">\n' % (i.name)
449                )
450                self.out.write('  <title role="details.title">Method Details</title>\n')
451                for m in i.methods:
452                    self.print_method(i, m)
453                self.out.write("</refsect1>\n")
454
455            if len(i.signals) > 0:
456                self.out.write(
457                    '<refsect1 role="details" id="gdbus-signals-%s">\n' % (i.name)
458                )
459                self.out.write('  <title role="details.title">Signal Details</title>\n')
460                for s in i.signals:
461                    self.print_signal(i, s)
462                self.out.write("</refsect1>\n")
463
464            if len(i.properties) > 0:
465                self.out.write(
466                    '<refsect1 role="details" id="gdbus-properties-%s">\n' % (i.name)
467                )
468                self.out.write(
469                    '  <title role="details.title">Property Details</title>\n'
470                )
471                for s in i.properties:
472                    self.print_property(i, s)
473                self.out.write("</refsect1>\n")
474
475            self.out.write("</refentry>\n")
476            self.out.write("\n")
477