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