]>
Commit | Line | Data |
---|---|---|
f613f81a KO |
1 | """ |
2 | Name: doxymlparser.py | |
3 | Author: Kevin Ollivier | |
4 | License: wxWidgets License | |
5 | """ | |
6 | ||
7 | __description__ = """ | |
8 | Takes the output of Doxygen XML and parses it to retrieve metadata about the classes and methods. | |
9 | ||
10 | To create the Doxygen XML files, from the wxWidgets/docs/doxygen directory, do: | |
11 | ||
12 | ./regen.sh xml | |
13 | ||
14 | To see the results from parsing a particular class, do: | |
15 | ||
16 | python doxymlparser.py --report out/xml/classwx_<whatever>.xml | |
17 | """ | |
18 | ||
19 | #!/usr/bin/env python | |
20 | import optparse | |
21 | import os | |
22 | import string | |
23 | ||
24 | import sys | |
25 | import types | |
0cbc1d02 KO |
26 | |
27 | from common import * | |
f613f81a KO |
28 | from xml.dom import minidom |
29 | ||
f613f81a KO |
30 | class ClassDefinition: |
31 | def __init__(self): | |
32 | self.name = "" | |
7f42e09b KO |
33 | self.constructors = [] |
34 | self.destructors = [] | |
f613f81a KO |
35 | self.methods = [] |
36 | self.brief_description = "" | |
37 | self.detailed_description = "" | |
38 | self.includes = [] | |
39 | self.bases = [] | |
7f42e09b | 40 | self.enums = {} |
f613f81a KO |
41 | |
42 | def __str__(self): | |
43 | str_repr = """ | |
44 | Class: %s | |
45 | Bases: %s | |
fc76d7df | 46 | Includes: %s |
f613f81a KO |
47 | Brief Description: |
48 | %s | |
49 | ||
50 | Detailed Description: | |
51 | %s | |
52 | """ % (self.name, string.join(self.bases, ", "), self.includes, self.brief_description, self.detailed_description) | |
53 | str_repr += "Methods:\n" | |
54 | ||
55 | for method in self.methods: | |
56 | str_repr += str(method) | |
57 | ||
58 | return str_repr | |
59 | ||
60 | class MethodDefinition: | |
61 | def __init__(self): | |
62 | self.name = "" | |
63 | self.return_type = "" | |
64 | self.argsstring = "" | |
65 | self.definition = "" | |
66 | self.params = [] | |
67 | self.brief_description = "" | |
68 | self.detailed_description = "" | |
69 | ||
70 | def __str__(self): | |
71 | str_repr = """ | |
72 | Method: %s | |
73 | Return Type: %s | |
74 | Params: %r | |
75 | Prototype: %s | |
76 | Brief Description: | |
77 | %s | |
78 | ||
79 | Detailed Description: | |
80 | %s | |
81 | """ % (self.name, self.return_type, self.params, self.definition + self.argsstring, self.brief_description, self.detailed_description) | |
82 | return str_repr | |
83 | ||
84 | def getTextValue(node, recursive=False): | |
85 | text = "" | |
86 | for child in node.childNodes: | |
0ed9af4e KO |
87 | if child.nodeType == child.ELEMENT_NODE and child.nodeName == "ref": |
88 | text += getTextValue(child) | |
f613f81a | 89 | if child.nodeType == child.TEXT_NODE: |
fc76d7df KO |
90 | # Add a space to ensure we have a space between qualifiers and parameter names |
91 | text += child.nodeValue.strip() + " " | |
f613f81a | 92 | |
fc76d7df | 93 | return text.strip() |
f613f81a KO |
94 | |
95 | def doxyMLToText(node): | |
96 | return text | |
97 | ||
98 | class DoxyMLParser: | |
e2075bf0 | 99 | def __init__(self, verbose = False): |
f613f81a | 100 | self.classes = [] |
e2075bf0 | 101 | self.verbose = verbose |
f613f81a | 102 | |
3fee01e9 KO |
103 | def find_class(self, name): |
104 | for aclass in self.classes: | |
105 | if aclass.name == name: | |
106 | return aclass | |
107 | ||
108 | return None | |
109 | ||
110 | def get_enums_and_functions(self, filename, aclass): | |
111 | file_path = os.path.dirname(filename) | |
112 | enum_filename = os.path.join(file_path, aclass.name[2:] + "_8h.xml") | |
113 | if os.path.exists(enum_filename): | |
114 | root = minidom.parse(enum_filename).documentElement | |
115 | for method in root.getElementsByTagName("memberdef"): | |
116 | if method.getAttribute("kind") == "enum": | |
117 | self.parse_enum(aclass, method, root) | |
118 | ||
119 | def is_derived_from_base(self, aclass, abase): | |
120 | base = get_first_value(aclass.bases) | |
121 | while base and base != "": | |
122 | ||
123 | if base == abase: | |
124 | return True | |
125 | ||
126 | parentclass = self.find_class(base) | |
127 | ||
128 | if parentclass: | |
129 | base = get_first_value(parentclass.bases) | |
130 | else: | |
131 | base = None | |
132 | ||
133 | return False | |
134 | ||
f613f81a KO |
135 | def parse(self, filename): |
136 | self.xmldoc = minidom.parse(filename).documentElement | |
137 | for node in self.xmldoc.getElementsByTagName("compounddef"): | |
7f42e09b KO |
138 | new_class = self.parse_class(node) |
139 | self.classes.append(new_class) | |
140 | self.get_enums_and_functions(filename, new_class) | |
f613f81a KO |
141 | |
142 | def parse_class(self, class_node): | |
143 | new_class = ClassDefinition() | |
7f42e09b | 144 | new_class.name = getTextValue(class_node.getElementsByTagName("compoundname")[0]) |
f613f81a | 145 | for node in class_node.childNodes: |
7f42e09b | 146 | if node.nodeName == "basecompoundref": |
f613f81a KO |
147 | new_class.bases.append(getTextValue(node)) |
148 | elif node.nodeName == "briefdescription": | |
149 | # let the post-processor determ | |
150 | new_class.brief_description = node.toxml() | |
151 | elif node.nodeName == "detaileddescription": | |
152 | new_class.detailed_description = node.toxml() | |
7f42e09b KO |
153 | elif node.nodeName == "includes": |
154 | new_class.includes.append(getTextValue(node)) | |
f613f81a KO |
155 | |
156 | self.parse_methods(new_class, class_node) | |
7f42e09b | 157 | |
f613f81a KO |
158 | return new_class |
159 | ||
7f42e09b KO |
160 | def parse_enum(self, new_class, enum, root): |
161 | enum_name = "" | |
162 | enum_values = [] | |
163 | ||
164 | for node in enum.childNodes: | |
165 | if node.nodeName == "name": | |
166 | enum_name = getTextValue(node) | |
167 | elif node.nodeName == "enumvalue": | |
168 | enum_values.append(getTextValue(node.getElementsByTagName("name")[0])) | |
169 | ||
170 | new_class.enums[enum_name] = enum_values | |
171 | ||
f613f81a | 172 | def parse_methods(self, new_class, root): |
7f42e09b | 173 | for method in root.getElementsByTagName("memberdef"): |
f613f81a KO |
174 | new_method = MethodDefinition() |
175 | for node in method.childNodes: | |
176 | if node.nodeName == "name": | |
177 | new_method.name = getTextValue(node) | |
178 | elif node.nodeName == "type": | |
179 | new_method.return_type = getTextValue(node) | |
180 | elif node.nodeName == "definition": | |
181 | new_method.definition = getTextValue(node) | |
182 | elif node.nodeName == "argsstring": | |
183 | new_method.argsstring = getTextValue(node) | |
184 | elif node.nodeName == "param": | |
185 | param = {} | |
186 | for child in node.childNodes: | |
187 | if child.nodeType == child.ELEMENT_NODE: | |
188 | param[child.nodeName] = getTextValue(child) | |
189 | new_method.params.append(param) | |
7f42e09b | 190 | |
e2075bf0 | 191 | if self.verbose: |
7f42e09b KO |
192 | print "Adding %s" % (new_method.name + new_method.argsstring) |
193 | ||
194 | if new_method.name == new_class.name: | |
195 | new_class.constructors.append(new_method) | |
196 | elif new_method.name == "~" + new_class.name: | |
197 | new_class.destructors.append(new_method) | |
198 | else: | |
199 | new_class.methods.append(new_method) | |
f613f81a KO |
200 | |
201 | if __name__ == "__main__": | |
e2075bf0 KO |
202 | option_dict = { |
203 | "report" : (False, "Print out the classes and methods found by this script."), | |
204 | "verbose" : (False, "Provide status updates and other information."), | |
205 | } | |
206 | ||
207 | parser = optparse.OptionParser(usage="usage: %prog [options] <doxyml files to parse>\n" + __description__, version="%prog 1.0") | |
208 | ||
209 | for opt in option_dict: | |
210 | default = option_dict[opt][0] | |
211 | ||
212 | action = "store" | |
213 | if type(default) == types.BooleanType: | |
214 | action = "store_true" | |
215 | parser.add_option("--" + opt, default=default, action=action, dest=opt, help=option_dict[opt][1]) | |
216 | ||
217 | options, arguments = parser.parse_args() | |
218 | ||
f613f81a KO |
219 | if len(arguments) < 1: |
220 | parser.print_usage() | |
221 | sys.exit(1) | |
222 | ||
e2075bf0 | 223 | doxyparse = DoxyMLParser(verbose = options.verbose) |
f613f81a KO |
224 | for arg in arguments: |
225 | doxyparse.parse(arg) | |
226 | ||
227 | if options.report: | |
228 | for aclass in doxyparse.classes: | |
229 | print str(aclass) | |
230 |