Initial checkin of new API implementation
[plcapi.git] / doc / DocBook.py
diff --git a/doc/DocBook.py b/doc/DocBook.py
new file mode 100755 (executable)
index 0000000..a62ef9b
--- /dev/null
@@ -0,0 +1,222 @@
+#!/usr/bin/python
+#
+# Generates a DocBook section documenting all PLCAPI methods on
+# stdout.
+#
+# Mark Huang <mlhuang@cs.princeton.edu>
+# Copyright (C) 2006 The Trustees of Princeton University
+#
+# $Id$
+#
+
+import xml.dom.minidom
+from xml.dom.minidom import Element, Text
+import codecs
+
+from PLC.API import PLCAPI
+from PLC.Method import *
+
+api = PLCAPI(None)
+
+# xml.dom.minidom.Text.writexml adds surrounding whitespace to textual
+# data when pretty-printing. Override this behavior.
+class TrimText(Text):
+    def writexml(self, writer, indent="", addindent="", newl=""):
+        Text.writexml(self, writer, "", "", "")
+
+class TrimTextElement(Element):
+    def writexml(self, writer, indent="", addindent="", newl=""):
+        writer.write(indent)
+        Element.writexml(self, writer, "", "", "")
+        writer.write(newl)
+
+class simpleElement(TrimTextElement):
+    """<tagName>text</tagName>"""
+    def __init__(self, tagName, text = None):
+        TrimTextElement.__init__(self, tagName)
+        if text is not None:
+            t = TrimText()
+            t.data = unicode(text)
+            self.appendChild(t)
+
+class paraElement(simpleElement):
+    """<para>text</para>"""
+    def __init__(self, text = None):
+        simpleElement.__init__(self, 'para', text)
+
+class blockquoteElement(Element):
+    """<blockquote><para>text...</para><para>...text</para></blockquote>"""
+    def __init__(self, text = None):
+        Element.__init__(self, 'blockquote')
+        if text is not None:
+            # Split on blank lines
+            lines = [line.strip() for line in text.strip().split("\n")]
+            lines = "\n".join(lines)
+            paragraphs = lines.split("\n\n")
+
+            for paragraph in paragraphs:
+                self.appendChild(paraElement(paragraph))
+
+class entryElement(simpleElement):
+    """<entry>text</entry>"""
+    def __init__(self, text = None):
+        simpleElement.__init__(self, 'entry', text)
+
+class rowElement(Element):
+    """
+    <row>
+      <entry>entries[0]</entry>
+      <entry>entries[1]</entry>
+      ...
+    </row>
+    """
+
+    def __init__(self, entries = None):
+        Element.__init__(self, 'row')
+        if entries:
+            for entry in entries:
+                if isinstance(entry, entryElement):
+                    self.appendChild(entry)
+                else:
+                    self.appendChild(entryElement(entry))
+
+class informaltableElement(Element):
+    """
+    <informaltable>
+      <tgroup>
+       <thead>
+         <row>
+           <entry>label1</entry>
+           <entry>label2</entry>
+           ...
+         </row>
+       </thead>
+       <tbody>
+         <row>
+           <entry>row1value1</entry>
+           <entry>row1value2</entry>
+           ...
+         </row>
+         ...
+       </tbody>
+      </tgroup>
+    </informaltable>
+    """
+
+    def __init__(self, head = None, rows = None):
+        Element.__init__(self, 'informaltable')
+        tgroup = Element('tgroup')
+        self.appendChild(tgroup)
+        if head:
+            thead = Element('thead')
+            tgroup.appendChild(thead)
+            if isinstance(head, rowElement):
+                thead.appendChild(head)
+            else:
+                thead.appendChild(rowElement(head))
+        if rows:
+            tbody = Element('tbody')
+            tgroup.appendChild(tbody)
+            for row in rows:
+                if isinstance(row, rowElement):
+                    tbody.appendChild(row)
+                else:
+                    tobdy.appendChild(rowElement(row))
+            cols = len(thead.childNodes[0].childNodes)
+            tgroup.setAttribute('cols', str(cols))
+
+def parameters(param, name, optional, default, doc, indent, step):
+    """Format a parameter into parameter row(s)."""
+
+    rows = []
+
+    row = rowElement()
+    rows.append(row)
+
+    # Parameter name
+    entry = entryElement()
+    entry.appendChild(simpleElement('literallayout', indent + name))
+    row.appendChild(entry)
+
+    # Parameter type
+    param_type = python_type(param)
+    row.appendChild(entryElement(xmlrpc_type(param_type)))
+
+    # Whether parameter is optional
+    row.appendChild(entryElement(str(bool(optional))))
+
+    # Parameter default
+    if optional and default is not None:
+        row.appendChild(entryElement(unicode(default)))
+    else:
+        row.appendChild(entryElement())
+
+    # Parameter documentation
+    row.appendChild(entryElement(doc))
+
+    # Indent the name of each sub-parameter
+    subparams = []
+    if isinstance(param, dict):
+        subparams = param.iteritems()
+    elif isinstance(param, Mixed):
+        subparams = [(name, subparam) for subparam in param]
+    elif isinstance(param, (list, tuple)):
+        subparams = [("", subparam) for subparam in param]
+
+    for name, subparam in subparams:
+        if isinstance(subparam, Parameter):
+            (optional, default, doc) = (subparam.optional, subparam.default, subparam.doc)
+        else:
+            # All named sub-parameters are optional if not otherwise specified
+            (optional, default, doc) = (True, None, "")
+        rows += parameters(subparam, name, optional, default, doc, indent + step, step)
+
+    return rows
+
+for method in api.methods:
+    func = api.callable(method)
+    (min_args, max_args, defaults) = func.args()
+
+    section = Element('section')
+    section.setAttribute('id', func.name)
+    section.appendChild(simpleElement('title', func.name))
+
+    para = paraElement('Status:')
+    para.appendChild(blockquoteElement(func.status))
+    section.appendChild(para)
+
+    prototype = "%s (%s)" % (method, ", ".join(max_args))
+    para = paraElement('Prototype:')
+    para.appendChild(blockquoteElement(prototype))
+    section.appendChild(para)
+
+    para = paraElement('Description:')
+    para.appendChild(blockquoteElement(func.__doc__))
+    section.appendChild(para)
+
+    para = paraElement('Parameters:')
+    blockquote = blockquoteElement()
+    para.appendChild(blockquote)
+    section.appendChild(para)
+
+    head = rowElement(['Name', 'Type', 'Optional', 'Default', 'Description'])
+    rows = []
+
+    indent = "  "
+    for name, param, default in zip(max_args, func.accepts, defaults):
+        optional = name not in min_args
+        if isinstance(param, Parameter):
+            doc = param.doc
+        else:
+            doc = ""
+        rows += parameters(param, name, optional, default, doc, "", indent)
+
+    if rows:
+        informaltable = informaltableElement(head, rows)
+        informaltable.setAttribute('frame', "none")
+        informaltable.setAttribute('rules', "rows")
+        blockquote.appendChild(informaltable)
+    else:
+        blockquote.appendChild(paraElement("None"))
+
+    print section.toprettyxml(encoding = "UTF-8")