Initial checkin of new API implementation
[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 #
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, default, 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     row.appendChild(entryElement(str(bool(optional))))
147
148     # Parameter default
149     if optional and default is not None:
150         row.appendChild(entryElement(unicode(default)))
151     else:
152         row.appendChild(entryElement())
153
154     # Parameter documentation
155     row.appendChild(entryElement(doc))
156
157     # Indent the name of each sub-parameter
158     subparams = []
159     if isinstance(param, dict):
160         subparams = param.iteritems()
161     elif isinstance(param, Mixed):
162         subparams = [(name, subparam) for subparam in param]
163     elif isinstance(param, (list, tuple)):
164         subparams = [("", subparam) for subparam in param]
165
166     for name, subparam in subparams:
167         if isinstance(subparam, Parameter):
168             (optional, default, doc) = (subparam.optional, subparam.default, subparam.doc)
169         else:
170             # All named sub-parameters are optional if not otherwise specified
171             (optional, default, doc) = (True, None, "")
172         rows += parameters(subparam, name, optional, default, doc, indent + step, step)
173
174     return rows
175
176 for method in api.methods:
177     func = api.callable(method)
178     (min_args, max_args, defaults) = func.args()
179
180     section = Element('section')
181     section.setAttribute('id', func.name)
182     section.appendChild(simpleElement('title', func.name))
183
184     para = paraElement('Status:')
185     para.appendChild(blockquoteElement(func.status))
186     section.appendChild(para)
187
188     prototype = "%s (%s)" % (method, ", ".join(max_args))
189     para = paraElement('Prototype:')
190     para.appendChild(blockquoteElement(prototype))
191     section.appendChild(para)
192
193     para = paraElement('Description:')
194     para.appendChild(blockquoteElement(func.__doc__))
195     section.appendChild(para)
196
197     para = paraElement('Parameters:')
198     blockquote = blockquoteElement()
199     para.appendChild(blockquote)
200     section.appendChild(para)
201
202     head = rowElement(['Name', 'Type', 'Optional', 'Default', 'Description'])
203     rows = []
204
205     indent = "  "
206     for name, param, default in zip(max_args, func.accepts, defaults):
207         optional = name not in min_args
208         if isinstance(param, Parameter):
209             doc = param.doc
210         else:
211             doc = ""
212         rows += parameters(param, name, optional, default, doc, "", indent)
213
214     if rows:
215         informaltable = informaltableElement(head, rows)
216         informaltable.setAttribute('frame', "none")
217         informaltable.setAttribute('rules', "rows")
218         blockquote.appendChild(informaltable)
219     else:
220         blockquote.appendChild(paraElement("None"))
221
222     print section.toprettyxml(encoding = "UTF-8")