get_documentation.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #! /usr/bin/env python
  2. # vin: sw=3 et:
  3. '''
  4. Copyright (C) 2012, Digium, Inc.
  5. Matt Jordan <mjordan@digium.com>
  6. This program is free software, distributed under the terms of
  7. the GNU General Public License Version 2.
  8. '''
  9. import sys
  10. import xml.dom.minidom
  11. def get_manager_event_method_type(candidate_string):
  12. if "ast_manager_event_multichan" in candidate_string:
  13. return "multichan"
  14. elif "ast_manager_event" in candidate_string:
  15. return "ast_manager_event"
  16. elif "manager_event" in candidate_string:
  17. return "manager_event"
  18. return ""
  19. def parse_manager_event_instance(xml_fragment):
  20. ''' Parse the information for a manager event
  21. Keyword Arguments:
  22. xml_fragment The XML fragment comment
  23. Returns:
  24. A well-formed XML fragment containing the comments passed in, as well as
  25. information obtained from the manager_event macro calls
  26. '''
  27. def __node_contains_parameter(node, parameter):
  28. ''' Return whether or not a node contains a given parameter name '''
  29. return any([n for n in node.getElementsByTagName("parameter")
  30. if __node_contains_attribute(n, parameter)])
  31. def __node_contains_attribute(node, attribute_name):
  32. ''' Return whether or not a node contains a given attribute name '''
  33. return any([attr for attr in node.attributes.items()
  34. if attr[1] == attribute_name])
  35. candidate_lines = []
  36. type = ""
  37. # Read the manager_event method call, which should occur after
  38. # the documentation block
  39. for line in sys.stdin:
  40. if len(line):
  41. candidate_lines.append(line)
  42. if ");" in line:
  43. break
  44. candidate_string = ''.join(candidate_lines)
  45. type = get_manager_event_method_type(candidate_string)
  46. if not type:
  47. # Unknown, return what we have
  48. return ''.join(xml_fragment)
  49. # strip off the macro name
  50. first_paren = candidate_string.index("(", 0)
  51. last_paren = candidate_string.rindex(");")
  52. candidate_string = candidate_string[first_paren + 1:last_paren]
  53. # split into parameter tokens
  54. func_parameter_tokens = candidate_string.split(',')
  55. if type == "manager_event" or type == "multichan":
  56. class_level = func_parameter_tokens[0].strip()
  57. event_type = func_parameter_tokens[1].strip()
  58. else:
  59. class_level = func_parameter_tokens[1].strip()
  60. event_type = func_parameter_tokens[2].strip()
  61. if type == "manager_event":
  62. event_parameters = func_parameter_tokens[2].strip()
  63. elif type == "ast_manager_event":
  64. event_parameters = func_parameter_tokens[3].strip()
  65. else:
  66. event_parameters = func_parameter_tokens[4].strip()
  67. parameter_tokens = event_parameters.replace("\"", "").split('\\r\\n')
  68. # Build the top level XML element information. Note that we temporarily
  69. # add the xi namespace in case any includes are used
  70. node_text = '<managerEvent language=\"%s\" name=\"%s\" xmlns:xi=\"%s\">'
  71. xml_fragment.insert(0, node_text % ('en_US',
  72. event_type.strip().replace("\"", ""),
  73. 'http://www.w3.org/2001/XInclude'))
  74. xml_fragment[1] = "<managerEventInstance class=\"%s\">" % (class_level)
  75. xml_fragment.insert(len(xml_fragment), "</managerEvent>")
  76. # Turn the XML into a DOM to manage the rest of the node manipulations
  77. dom = xml.dom.minidom.parseString(''.join(xml_fragment))
  78. # Get the syntax node if we have one; otherwise make one
  79. instance = dom.getElementsByTagName("managerEventInstance")[0]
  80. syntax = instance.getElementsByTagName("syntax")
  81. if not syntax:
  82. syntax = dom.createElement("syntax")
  83. instance.appendChild(syntax)
  84. # Move any existing parameter nodes over
  85. for node in instance.getElementsByTagName("parameter"):
  86. syntax.appendChild(node.cloneNode(True))
  87. instance.removeChild(node)
  88. else:
  89. syntax = syntax[0]
  90. # Add parameters found in the method invocation that were not previously
  91. # documented
  92. for parameter in parameter_tokens:
  93. if not len(parameter):
  94. continue
  95. index = parameter.find(':')
  96. if index < 0:
  97. index = len(parameter)
  98. parameter = (parameter[:index].strip().replace("\"", ""))
  99. if ('%s' not in parameter and
  100. not __node_contains_parameter(syntax, parameter)):
  101. e = dom.createElement("parameter")
  102. e.setAttribute('name', parameter)
  103. syntax.appendChild(e)
  104. return dom.toxml().replace("<?xml version=\"1.0\" ?>", "").replace(
  105. 'xmlns:xi="http://www.w3.org/2001/XInclude"', '')
  106. def main(argv=None):
  107. if argv is None:
  108. argv = sys.argv
  109. in_doc = False
  110. xml_fragment = []
  111. xml = []
  112. line_number = 0
  113. for line in sys.stdin:
  114. # Note: multiple places may have to read a line, so iterating over
  115. # readlines isn't possible. Break when a null line is returned
  116. line_number += 1
  117. if not line:
  118. break
  119. line = line.strip()
  120. if ("/*** DOCUMENTATION" in line):
  121. in_doc = True
  122. elif ("***/" in line and in_doc):
  123. # Depending on what we're processing, determine if we need to do
  124. # any additional work
  125. in_doc = False
  126. if not xml_fragment:
  127. # Nothing read, move along
  128. continue
  129. if "<managerEventInstance>" in xml_fragment[0]:
  130. xml.append(parse_manager_event_instance(xml_fragment))
  131. else:
  132. xml.append(''.join(xml_fragment))
  133. xml_fragment = []
  134. elif (in_doc):
  135. xml_fragment.append("%s\n" % line)
  136. sys.stdout.write(''.join(xml))
  137. return 0
  138. if __name__ == "__main__":
  139. sys.exit(main() or 0)