#/**
# *
# * Licensed under the Apache License, Version 2.0 (the "License");
# * you may not use this file except in compliance with the License.
# * You may obtain a copy of the License at
# *
# * http://www.apache.org/licenses/LICENSE-2.0
# *
# * Unless required by applicable law or agreed to in writing, software
# * distributed under the License is distributed on an "AS IS" BASIS,
# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# * See the License for the specific language governing permissions and
# * limitations under the License.
# *
# */
import logging
import re
from xml.dom import minidom
class WikiTextConverter:
logger = logging.getLogger(__name__)
CLASS_HEADER = """
{{NeedsExamples}}
__TOC__
"""
METHODS_HEADER = "== Methods ==\n\n"
FIELDS_HEADER = "== Fields ==\n\n"
def __init__(self, specVersion):
self.specVersion = specVersion
# Converts the given spec node, which represents a class, into text for a wiki page
#
# param specClass a minidom.Node containing class info in spec format
# returns a string containing wiki markup for the class
def convertClass(self, specClass):
# add the header
wikiText = self.CLASS_HEADER
# add the class name
className = specClass.getAttribute("title")
wikiText += '' + className + '\n\n'
# add the class description
for node in specClass.getElementsByTagName("t"):
if node.parentNode.isSameNode(specClass):
text = node.toxml().strip()[3:-4].strip() # strip the surrounding and tags
text = self.__removeWhitespace(text)
text = self.__convertLinks(text)
wikiText += text.strip() + "\n\n"
break
# add the methods
wikiText += self.METHODS_HEADER
for node in specClass.getElementsByTagName("section"):
# Ignore elements that are children of subnodes
if not node.parentNode.isSameNode(specClass):
continue
foundMethods = False
if node.getAttribute("title") == "Method Details":
foundMethods = True
for methodNode in node.getElementsByTagName("section"):
# Ignore elements that are children of subnodes
if not methodNode.parentNode.isSameNode(node):
continue
self.logger.info("Parsing %s method" % methodNode.getAttribute("anchor"))
wikiText += "===%s===\n" % methodNode.getAttribute("anchor")
wikiText += self.__getMethodSignature(methodNode)
wikiText += self.__getMethodParameters(methodNode)
wikiText += self.__getMethodReturns(methodNode)
wikiText += self.__getMethodDescription(methodNode)
break
if not foundMethods:
wikiText += "'''None'''\n\n"
return wikiText
def __getMethodSignature(self, methodNode):
text = methodNode.getElementsByTagName('t')[0].toxml()[3:-4]
text = self.__removeWhitespace(text)
text = re.sub(r'Type: {((?s).*)} (.*)$', r'\1 \2', text)
text = self.__convertLinks(text)
return ':;%s\n\n' % text
# Finds the parameters in the givenMethod node and converts them to wiki syntax. There are two
# places the parameters can appear. First (and most common), is in a child element of the
# method node that starts with 'Parameters:' (ignoring whitespace). Second (e.g. makeRequest),
# is in a child element o the method node (within a chilc
# node).
#
# param methodNode a minidom.Node containing method info in spec format
# returns a String containing the wikified text for listing the parameters
def __getMethodParameters(self, methodNode):
found = False;
# Check for a child element that starts with 'Pareamters:'
for node in methodNode.getElementsByTagName("t"):
text = node.toxml().strip()[3:-4].strip() # strip the surrounding and tags
if text.startswith("Parameters:"):
text = text[11:] # Strip the leading 'Parameters:' string
found = True;
break
# Check for a child node
if not found:
for node in methodNode.getElementsByTagName("section"):
if node.getAttribute('title') == 'Parameters':
# The parameters contains a elemented with the of parameters
tNode = node.getElementsByTagName("t")[0]
text = tNode.getElementsByTagName('texttable')[0].toxml()
found = True;
# If there are no parameters, don't include the Parameters heading in the wiki
if not found:
return ''
parameterString = ":Parameters\n:"
parameterString += self.__convertTable(text).strip() + "\n\n"
return parameterString
# Parses the first child node of the method node that starts with 'Returns:'
def __getMethodReturns(self, methodNode):
for node in methodNode.getElementsByTagName("t"):
text = node.toxml().strip()[3:-4].strip() # strip the '' and '' tags
if text.startswith("Returns:"):
returnsString = ":Returns\n:"
text = text[8:] # String the 'Returns:'
returnsString += self.__convertTable(text).strip() + "\n\n"
return returnsString
return ""
# Finds the method description and wikifies it. Usually the description is the first
# child of the method node and any sub elements. Sometimes (e.g. makeRequest) the
# description is longer and includes elements after the initial .
#
# param methodNode a minidom.Node containing the spec-formatted info about the method
# returns a String containing the wikified version of the text
def __getMethodDescription(self, methodNode):
wikiDesc = ""
for node in methodNode.getElementsByTagName("t"):
text = node.toxml()[3:-4].strip() # strip the '' and '' tags
if text.startswith("Description:"):
wikiDesc = text[12:].strip()
wikiDesc = self.__convertCode(wikiDesc)
wikiDesc = self.__convertLinks(wikiDesc)
wikiDesc = re.sub(r'', r'
', wikiDesc)
wikiDesc = re.sub(r'
', r'', wikiDesc)
wikiDesc = self.__removeWhitespace(wikiDesc)
break
for node in methodNode.getElementsByTagName("section"):
if node.getAttribute('title') == 'Parameters':
continue
wikiDesc += self.__convertExtendedDescription(node)
wikiDesc = ":'''Description'''\n
""%s
\n" % wikiDesc
#if methodNode.getAttribute('title') == 'makeRequest':
# self.logger.info(wikiDesc)
return wikiDesc
# Parses a element that contains an extended method description (e.g. makeRequest)
# and returns a wikified string of text.
def __convertExtendedDescription(self, descNode):
text = self.__removeLeadingWhitespace(descNode.toxml())
text = self.__convertLinks(text)
text = self.__convertLists(text)
text = re.sub(r'', r"'''\1'''", text)
text = re.sub(r'', r'
', text)
text = re.sub(r'
', r'', text)
text = re.sub(r'', r'', text)
return text
# Creates a new miniDom with a wikified version of the given list
def __convertLists(self, descNodeText):
descNode = minidom.parseString(descNodeText)
for specListNode in descNode.getElementsByTagName('list'):
if specListNode.getAttribute('style') == 'numbers':
wikiListNode = minidom.Element('ol')
else:
wikiListNode = minidom.Element('ul')
text = ''
for node in specListNode.childNodes:
if not node.hasChildNodes():
continue # ignore any Text nodes
if node.tagName == 't':
itemText = minidom.Text()
itemText.data = self.__removeLeadingWhitespace(node.firstChild.data).strip()
if node.getAttribute('hangText'):
itemText.data = "'''%s''' - %s" % (node.getAttribute('hangText'), itemText.data)
listItem = minidom.Element('li')
listItem.appendChild(itemText)
wikiListNode.appendChild(listItem)
else:
wikiListNode.appendChild(node)
descNodeText = re.sub(r'', wikiListNode.toxml(), descNodeText)
return descNodeText
# Converts a spec table to a wiki table
#
# param text a string containing the spec-formatted table
# returns a string containing the wiki-formatted table
def __convertTable(self, text):
text = re.sub(r'((?s).*)',
r':
\1
', text)
text = re.sub(r']*>', r' ', text)
text = self.__convertLinks(text)
text = text.replace("", "
")
text = text.replace("", "
")
text = text.replace("", "
")
text = text.replace("", "
")
numColumns = text.count("
")
numCells = text.count("
")
# enclose the
's in a
text = text[:text.find("
")] + "
" + text[text.find("
"):]
text = text[:text.rfind("
")+5] + "
" + text[text.rfind("")+5:]
# every nth time we find a
, insert a
(where n = numColumns)
count = 0 # track the number of
's processed
idx = 0 # the index to start searching for the next
while (count < numCells):
idx = text.find("
", idx)
if count % numColumns == 0:
text = text[:idx] + "
" + text[idx:]
idx += 4 # jump past the
we inserted
idx += 4 # jump past the
we just found
count += 1
# every nth time we find a
, insert a
(where n = numColumns)
count = 0 # track the number of
's processed
idx = 0 # the index to start searching for the next
while (count < numCells):
idx = text.find("
", idx)
if count % numColumns == numColumns - 1 :
text = text[:idx+5] + "" + text[idx+5:]
idx += 4 # jump past the we inserted
idx += 4 # jump past the we just found
count += 1
# fix whitespace issues
# - remove whitespace after newlines
# - get rid of newlines
# - remove whitespace after
's
idx = text.find("\n", 0)
while (idx>0):
text = text[:idx + 1] + text[idx + 1:].strip() # add 1 to skip the \n we just found
idx = text.find("\n", idx + 1) # add 1 to skip the \n we just found
text = text.replace("\n", " ")
idx = text.find("
", 0)
while (idx>0):
text = text[:idx + 4] + text[idx + 4:].strip() # add 4 to skip the
we just found
idx = text.find("
", idx + 4) # add 4 to skip the
we just found
return text
# Converts xref and eref tags to wiki links.
def __convertLinks(self, text):
#text = re.sub(r'([^<]*)',
# r'[[\1 ' + self.specVersion + r' | \2]]', text)
text = re.sub(r'((?s)[^<]*)',
r'[[\1 ' + self.specVersion + r' | \2]]', text)
text = re.sub(r'((?s)[^<]*)', r'[\1 \2]', text)
return text
# Converts the spec representation of a code block to wikified text
def __convertCode(self, text):
text = re.sub(r'\n((?s).*)', r'\1', text)
text = re.sub(r'((?s).*)', r'', text)
text = re.sub(r'\n((?s).*)',
r'\1', text)
return text
# Converts the given spec fields into text for a wiki page
#
# param specFields a minidom.Node containing field info in spec format
# returns a string containing wiki markup for the fields
def convertFields(self, specFields):
# Get the name of the class for the section header
title = specFields.getAttribute("title")
wikitext = "==%s==\n" % title
# Get the high level description from the first child
text = specFields.getElementsByTagName("t")[0].toxml()
text = self.__convertLinks(text)
text = self.__removeWhitespace(text)
wikitext += text.strip() + "\n\n"
# Iterate over the subsections of 'Field Details'
for node in specFields.getElementsByTagName("section"):
if not node.parentNode.isSameNode(specFields):
continue
if node.getAttribute("title") == "Field Details":
for fieldNode in node.getElementsByTagName("section"):
wikitext += "=== %s ===\n" % fieldNode.getAttribute('anchor')
text = fieldNode.toxml()
text = re.sub(r'', r'', text)
text = re.sub(r'', r'', text)
text = re.sub(r'.*', r'', text, 1)
text = self.__convertLinks(text)
text = self.__removeWhitespace(text) + "\n"
wikitext += ":" + text
return wikitext
# Takes out all whitespace on each line
def __removeWhitespace(self, text):
text = text.strip()
text = text.replace("","").replace("","")
lines = text.split("\n")
toReturn = ""
for line in lines:
line = line.strip()
if line.startswith("Description:"):
line = line[13:].strip()
toReturn += line + " "
return toReturn.strip()
def __removeLeadingWhitespace(self, wikiString):
toReturn = ''
lines = wikiString.split('\n')
for line in lines:
toReturn += line.strip() + '\n'
return toReturn