- add introduction and general discussion
[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: DocBook.py,v 1.2 2006/10/25 14:32:01 mlhuang Exp $
10 #
11
12 import xml.dom.minidom
13 from xml.dom.minidom import Element, Text
14 import codecs
15
16 from PLC.API import PLCAPI
17 from PLC.Method import *
18
19 api = PLCAPI(None)
20
21 # xml.dom.minidom.Text.writexml adds surrounding whitespace to textual
22 # data when pretty-printing. Override this behavior.
23 class TrimText(Text):
24     def writexml(self, writer, indent="", addindent="", newl=""):
25         Text.writexml(self, writer, "", "", "")
26
27 class TrimTextElement(Element):
28     def writexml(self, writer, indent="", addindent="", newl=""):
29         writer.write(indent)
30         Element.writexml(self, writer, "", "", "")
31         writer.write(newl)
32
33 class simpleElement(TrimTextElement):
34     """<tagName>text</tagName>"""
35     def __init__(self, tagName, text = None):
36         TrimTextElement.__init__(self, tagName)
37         if text is not None:
38             t = TrimText()
39             t.data = unicode(text)
40             self.appendChild(t)
41
42 class paraElement(simpleElement):
43     """<para>text</para>"""
44     def __init__(self, text = None):
45         simpleElement.__init__(self, 'para', text)
46
47 class blockquoteElement(Element):
48     """<blockquote><para>text...</para><para>...text</para></blockquote>"""
49     def __init__(self, text = None):
50         Element.__init__(self, 'blockquote')
51         if text is not None:
52             # Split on blank lines
53             lines = [line.strip() for line in text.strip().split("\n")]
54             lines = "\n".join(lines)
55             paragraphs = lines.split("\n\n")
56
57             for paragraph in paragraphs:
58                 self.appendChild(paraElement(paragraph))
59
60 class entryElement(simpleElement):
61     """<entry>text</entry>"""
62     def __init__(self, text = None):
63         simpleElement.__init__(self, 'entry', text)
64
65 class rowElement(Element):
66     """
67     <row>
68       <entry>entries[0]</entry>
69       <entry>entries[1]</entry>
70       ...
71     </row>
72     """
73
74     def __init__(self, entries = None):
75         Element.__init__(self, 'row')
76         if entries:
77             for entry in entries:
78                 if isinstance(entry, entryElement):
79                     self.appendChild(entry)
80                 else:
81                     self.appendChild(entryElement(entry))
82
83 class informaltableElement(Element):
84     """
85     <informaltable>
86       <tgroup>
87         <thead>
88           <row>
89             <entry>label1</entry>
90             <entry>label2</entry>
91             ...
92           </row>
93         </thead>
94         <tbody>
95           <row>
96             <entry>row1value1</entry>
97             <entry>row1value2</entry>
98             ...
99           </row>
100           ...
101         </tbody>
102       </tgroup>
103     </informaltable>
104     """
105
106     def __init__(self, head = None, rows = None):
107         Element.__init__(self, 'informaltable')
108         tgroup = Element('tgroup')
109         self.appendChild(tgroup)
110         if head:
111             thead = Element('thead')
112             tgroup.appendChild(thead)
113             if isinstance(head, rowElement):
114                 thead.appendChild(head)
115             else:
116                 thead.appendChild(rowElement(head))
117         if rows:
118             tbody = Element('tbody')
119             tgroup.appendChild(tbody)
120             for row in rows:
121                 if isinstance(row, rowElement):
122                     tbody.appendChild(row)
123                 else:
124                     tobdy.appendChild(rowElement(row))
125             cols = len(thead.childNodes[0].childNodes)
126             tgroup.setAttribute('cols', str(cols))
127
128 def parameters(param, name, optional, doc, indent, step):
129     """Format a parameter into parameter row(s)."""
130
131     rows = []
132
133     row = rowElement()
134     rows.append(row)
135
136     # Parameter name
137     entry = entryElement()
138     entry.appendChild(simpleElement('literallayout', indent + name))
139     row.appendChild(entry)
140
141     # Parameter type
142     param_type = python_type(param)
143     row.appendChild(entryElement(xmlrpc_type(param_type)))
144
145     # Whether parameter is optional
146     if optional is not None:
147         row.appendChild(entryElement(str(bool(optional))))
148     else:
149         row.appendChild(entryElement(""))
150
151     # Parameter documentation
152     row.appendChild(entryElement(doc))
153
154     # Indent the name of each sub-parameter
155     subparams = []
156     if isinstance(param, dict):
157         subparams = param.iteritems()
158     elif isinstance(param, Mixed):
159         subparams = [(name, subparam) for subparam in param]
160     elif isinstance(param, (list, tuple)):
161         subparams = [("", subparam) for subparam in param]
162
163     for name, subparam in subparams:
164         if isinstance(subparam, Parameter):
165             (optional, doc) = (subparam.optional, subparam.doc)
166         else:
167             (optional, doc) = (None, "")
168         rows += parameters(subparam, name, optional, doc, indent + step, step)
169
170     return rows
171
172 for method in api.methods:
173     func = api.callable(method)
174     (min_args, max_args, defaults) = func.args()
175
176     section = Element('section')
177     section.setAttribute('id', func.name)
178     section.appendChild(simpleElement('title', func.name))
179
180     para = paraElement('Status:')
181     para.appendChild(blockquoteElement(func.status))
182     section.appendChild(para)
183
184     prototype = "%s (%s)" % (method, ", ".join(max_args))
185     para = paraElement('Prototype:')
186     para.appendChild(blockquoteElement(prototype))
187     section.appendChild(para)
188
189     para = paraElement('Description:')
190     para.appendChild(blockquoteElement(func.__doc__))
191     section.appendChild(para)
192
193     para = paraElement('Parameters:')
194     blockquote = blockquoteElement()
195     para.appendChild(blockquote)
196     section.appendChild(para)
197
198     head = rowElement(['Name', 'Type', 'Optional', 'Description'])
199     rows = []
200
201     indent = "  "
202     for name, param, default in zip(max_args, func.accepts, defaults):
203         optional = name not in min_args
204         if isinstance(param, Parameter):
205             doc = param.doc
206         else:
207             doc = ""
208         rows += parameters(param, name, optional, doc, "", indent)
209
210     if rows:
211         informaltable = informaltableElement(head, rows)
212         informaltable.setAttribute('frame', "none")
213         informaltable.setAttribute('rules', "rows")
214         blockquote.appendChild(informaltable)
215     else:
216         blockquote.appendChild(paraElement("None"))
217
218     para = paraElement('Returns:')
219     blockquote = blockquoteElement()
220     para.appendChild(blockquote)
221     section.appendChild(para)
222
223     head = rowElement(['Name', 'Type', 'Optional', 'Description'])
224     if isinstance(func.returns, Parameter):
225         doc = func.returns.doc
226     else:
227         doc = ""
228     indent = "  "
229     rows = parameters(func.returns, "", None, doc, "", indent)
230     informaltable = informaltableElement(head, rows)
231     informaltable.setAttribute('frame', "none")
232     informaltable.setAttribute('rules', "rows")
233     blockquote.appendChild(informaltable)
234
235     print section.toprettyxml(encoding = "UTF-8")