first draft for the myplc-docs package (cross-module change)
[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.Parameter import Parameter, Mixed, xmlrpc_type, python_type
17
18 # xml.dom.minidom.Text.writexml adds surrounding whitespace to textual
19 # data when pretty-printing. Override this behavior.
20 class TrimText(Text):
21     """text"""
22     def __init__(self, text = None):
23         self.data = unicode(text)
24
25     def writexml(self, writer, indent="", addindent="", newl=""):
26         Text.writexml(self, writer, "", "", "")
27
28 class TrimTextElement(Element):
29     """<tagName>text</tagName>"""
30     def __init__(self, tagName, text = None):
31         Element.__init__(self, tagName)
32         if text is not None:
33             self.appendChild(TrimText(text))
34
35     def writexml(self, writer, indent="", addindent="", newl=""):
36         writer.write(indent)
37         Element.writexml(self, writer, "", "", "")
38         writer.write(newl)
39
40 class simpleElement(TrimTextElement): pass
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 def param_type(param):
61     """Return the XML-RPC type of a parameter."""
62     if isinstance(param, Mixed) and len(param):
63         subtypes = [param_type(subparam) for subparam in param]
64         return " or ".join(subtypes)
65     elif isinstance(param, (list, tuple, set)) and len(param):
66         return "array of " + " or ".join([param_type(subparam) for subparam in param])
67     else:
68         return xmlrpc_type(python_type(param))
69
70 class paramElement(Element):
71     """An optionally named parameter."""
72     def __init__(self, name, param):
73         # <listitem>
74         Element.__init__(self, 'listitem')
75
76         description = Element('para')
77
78         if name:
79             description.appendChild(simpleElement('parameter', name))
80             description.appendChild(TrimText(": "))
81
82         description.appendChild(TrimText(param_type(param)))
83
84         if isinstance(param, (list, tuple, set)) and len(param) == 1:
85             param = param[0]
86
87         if isinstance(param, Parameter):
88             description.appendChild(TrimText(", " + param.doc))
89             param = param.type
90
91         self.appendChild(description)
92
93         if isinstance(param, dict):
94             itemizedlist = Element('itemizedlist')
95             self.appendChild(itemizedlist)
96             for name, subparam in param.iteritems():
97                 itemizedlist.appendChild(paramElement(name, subparam))
98
99         elif isinstance(param, (list, tuple, set)) and len(param):
100             itemizedlist = Element('itemizedlist')
101             self.appendChild(itemizedlist)
102             for subparam in param:
103                 itemizedlist.appendChild(paramElement(None, subparam))
104
105 class DocBook:
106     
107     def __init__ (self,functions_list):
108         self.functions_list = functions_list
109
110     def Process (self):
111         
112         for func in self.functions_list:
113             method = func.name
114
115             if func.status == "deprecated":
116                 continue
117
118             (min_args, max_args, defaults) = func.args()
119
120             section = Element('section')
121             section.setAttribute('id', func.name)
122             section.appendChild(simpleElement('title', func.name))
123
124             prototype = "%s (%s)" % (method, ", ".join(max_args))
125             para = paraElement('Prototype:')
126             para.appendChild(blockquoteElement(prototype))
127             section.appendChild(para)
128
129             para = paraElement('Description:')
130             para.appendChild(blockquoteElement(func.__doc__))
131             section.appendChild(para)
132
133             para = paraElement('Allowed Roles:')
134             para.appendChild(blockquoteElement(", ".join(func.roles)))
135             section.appendChild(para)
136
137             section.appendChild(paraElement('Parameters:'))
138             params = Element('itemizedlist')
139             if func.accepts:
140                 for name, param, default in zip(max_args, func.accepts, defaults):
141                     params.appendChild(paramElement(name, param))
142             else:
143                 listitem = Element('listitem')
144                 listitem.appendChild(paraElement('None'))
145                 params.appendChild(listitem)
146             section.appendChild(params)
147
148             section.appendChild(paraElement('Returns:'))
149             returns = Element('itemizedlist')
150             returns.appendChild(paramElement(None, func.returns))
151             section.appendChild(returns)
152
153             print section.toprettyxml(encoding = "UTF-8")