Fix version output when missing.
[plcapi.git] / doc / DocBook.py
1 #!/usr/bin/python
2 #
3 # Generates a DocBook section documenting all PLCAPI methods on
4 # stdout.
5 #
6 # Mark Huang <mlhuang@cs.princeton.edu>
7 # Copyright (C) 2006 The Trustees of Princeton University
8 #
9 # $Id$
10 # $URL$
11 #
12
13 import xml.dom.minidom
14 from xml.dom.minidom import Element, Text
15 import codecs
16
17 from PLC.Parameter import Parameter, Mixed, xmlrpc_type, python_type
18
19 # xml.dom.minidom.Text.writexml adds surrounding whitespace to textual
20 # data when pretty-printing. Override this behavior.
21 class TrimText(Text):
22     """text"""
23     def __init__(self, text = None):
24         self.data = unicode(text)
25
26     def writexml(self, writer, indent="", addindent="", newl=""):
27         Text.writexml(self, writer, "", "", "")
28
29 class TrimTextElement(Element):
30     """<tagName>text</tagName>"""
31     def __init__(self, tagName, text = None):
32         Element.__init__(self, tagName)
33         if text is not None:
34             self.appendChild(TrimText(text))
35
36     def writexml(self, writer, indent="", addindent="", newl=""):
37         writer.write(indent)
38         Element.writexml(self, writer, "", "", "")
39         writer.write(newl)
40
41 class simpleElement(TrimTextElement): pass
42
43 class paraElement(simpleElement):
44     """<para>text</para>"""
45     def __init__(self, text = None):
46         simpleElement.__init__(self, 'para', text)
47
48 class blockquoteElement(Element):
49     """<blockquote><para>text...</para><para>...text</para></blockquote>"""
50     def __init__(self, text = None):
51         Element.__init__(self, 'blockquote')
52         if text is not None:
53             # Split on blank lines
54             lines = [line.strip() for line in text.strip().split("\n")]
55             lines = "\n".join(lines)
56             paragraphs = lines.split("\n\n")
57
58             for paragraph in paragraphs:
59                 self.appendChild(paraElement(paragraph))
60
61 def param_type(param):
62     """Return the XML-RPC type of a parameter."""
63     if isinstance(param, Mixed) and len(param):
64         subtypes = [param_type(subparam) for subparam in param]
65         return " or ".join(subtypes)
66     elif isinstance(param, (list, tuple, set)) and len(param):
67         return "array of " + " or ".join([param_type(subparam) for subparam in param])
68     else:
69         return xmlrpc_type(python_type(param))
70
71 class paramElement(Element):
72     """An optionally named parameter."""
73     def __init__(self, name, param):
74         # <listitem>
75         Element.__init__(self, 'listitem')
76
77         description = Element('para')
78
79         if name:
80             description.appendChild(simpleElement('parameter', name))
81             description.appendChild(TrimText(": "))
82
83         description.appendChild(TrimText(param_type(param)))
84
85         if isinstance(param, (list, tuple, set)) and len(param) == 1:
86             param = param[0]
87
88         if isinstance(param, Parameter):
89             description.appendChild(TrimText(", " + param.doc))
90             param = param.type
91
92         self.appendChild(description)
93
94         if isinstance(param, dict):
95             itemizedlist = Element('itemizedlist')
96             self.appendChild(itemizedlist)
97             for name, subparam in param.iteritems():
98                 itemizedlist.appendChild(paramElement(name, subparam))
99
100         elif isinstance(param, (list, tuple, set)) and len(param):
101             itemizedlist = Element('itemizedlist')
102             self.appendChild(itemizedlist)
103             for subparam in param:
104                 itemizedlist.appendChild(paramElement(None, subparam))
105
106 class DocBook:
107     
108     def __init__ (self,functions_list):
109         self.functions_list = functions_list
110
111     def Process (self):
112         
113         for func in self.functions_list:
114             method = func.name
115
116             if func.status == "deprecated":
117                 continue
118
119             (min_args, max_args, defaults) = func.args()
120
121             section = Element('section')
122             section.setAttribute('id', func.name)
123             section.appendChild(simpleElement('title', func.name))
124
125             prototype = "%s (%s)" % (method, ", ".join(max_args))
126             para = paraElement('Prototype:')
127             para.appendChild(blockquoteElement(prototype))
128             section.appendChild(para)
129
130             para = paraElement('Description:')
131             para.appendChild(blockquoteElement(func.__doc__))
132             section.appendChild(para)
133
134             para = paraElement('Allowed Roles:')
135             para.appendChild(blockquoteElement(", ".join(func.roles)))
136             section.appendChild(para)
137
138             section.appendChild(paraElement('Parameters:'))
139             params = Element('itemizedlist')
140             if func.accepts:
141                 for name, param, default in zip(max_args, func.accepts, defaults):
142                     params.appendChild(paramElement(name, param))
143             else:
144                 listitem = Element('listitem')
145                 listitem.appendChild(paraElement('None'))
146                 params.appendChild(listitem)
147             section.appendChild(params)
148
149             section.appendChild(paraElement('Returns:'))
150             returns = Element('itemizedlist')
151             returns.appendChild(paramElement(None, func.returns))
152             section.appendChild(returns)
153
154             print section.toprettyxml(encoding = "UTF-8")