Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
807d02702589efec6e4bcd88dbd9585384db8575
[simgrid.git] / docs / source / _ext / javasphinx / compiler.py
1 #
2 # Copyright 2012-2015 Bronto Software, Inc. and contributors
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16
17 import javalang
18
19 import javasphinx.formatter as formatter
20 import javasphinx.util as util
21 import javasphinx.htmlrst as htmlrst
22
23 class JavadocRestCompiler(object):
24     """ Javadoc to ReST compiler. Builds ReST documentation from a Java syntax
25     tree. """
26
27     def __init__(self, filter=None, member_headers=True, parser='lxml'):
28         if filter:
29             self.filter = filter
30         else:
31             self.filter = self.__default_filter
32
33         self.converter = htmlrst.Converter(parser)
34
35         self.member_headers = member_headers
36
37     def __default_filter(self, node):
38         """Excludes private members and those tagged "@hide" / "@exclude" in their
39         docblocks.
40
41         """
42
43         if not isinstance(node, javalang.tree.Declaration):
44             return False
45
46         if 'private' in node.modifiers:
47             return False
48
49         if isinstance(node, javalang.tree.Documented) and node.documentation:
50             doc = javalang.javadoc.parse(node.documentation)
51             if 'hide' in doc.tags or 'exclude' in doc.tags:
52                 return False
53
54         return True
55
56     def __html_to_rst(self, s):
57         return self.converter.convert(s)
58
59     def __output_doc(self, documented):
60         if not isinstance(documented, javalang.tree.Documented):
61             raise ValueError('node not documented')
62
63         output = util.Document()
64
65         if not documented.documentation:
66             return output
67
68         doc = javalang.javadoc.parse(documented.documentation)
69
70         if doc.description:
71             output.add(self.__html_to_rst(doc.description))
72             output.clear()
73
74         if doc.authors:
75             output.add_line(':author: %s' % (self.__html_to_rst(', '.join(doc.authors)),))
76
77         for name, value in doc.params:
78             output.add_line(':param %s: %s' % (name, self.__html_to_rst(value)))
79
80         for exception in doc.throws:
81             description = doc.throws[exception]
82             output.add_line(':throws %s: %s' % (exception, self.__html_to_rst(description)))
83
84         if doc.return_doc:
85             output.add_line(':return: %s' % (self.__html_to_rst(doc.return_doc),))
86
87         if doc.tags.get('see'):
88             output.clear()
89
90             see_also = ', '.join(self.__output_see(see) for see in doc.tags['see'])
91             output.add_line('**See also:** %s' % (see_also,))
92
93         return output
94
95     def __output_see(self, see):
96         """ Convert the argument to a @see tag to rest """
97
98         if see.startswith('<a href'):
99             # HTML link -- <a href="...">...</a>
100             return self.__html_to_rst(see)
101         elif '"' in see:
102             # Plain text
103             return see
104         else:
105             # Type reference (default)
106             return ':java:ref:`%s`' % (see.replace('#', '.').replace(' ', ''),)
107
108     def compile_type(self, declaration):
109         signature = util.StringBuilder()
110         formatter.output_declaration(declaration, signature)
111
112         doc = self.__output_doc(declaration)
113
114         directive = util.Directive('java:type', signature.build())
115         directive.add_content(doc)
116
117         return directive
118
119     def compile_enum_constant(self, enum, constant):
120         signature = util.StringBuilder()
121
122         for annotation in constant.annotations:
123             formatter.output_annotation(annotation, signature)
124
125         # All enum constants are public, static, and final
126         signature.append('public static final ')
127         signature.append(enum)
128         signature.append(' ')
129         signature.append(constant.name)
130
131         doc = self.__output_doc(constant)
132
133         directive = util.Directive('java:field', signature.build())
134         directive.add_content(doc)
135
136         return directive
137
138     def compile_field(self, field):
139         signature = util.StringBuilder()
140
141         for annotation in field.annotations:
142             formatter.output_annotation(annotation, signature)
143
144         formatter.output_modifiers(field.modifiers, signature)
145         signature.append(' ')
146
147         formatter.output_type(field.type, signature)
148         signature.append(' ')
149         signature.append(field.declarators[0].name)
150
151         doc = self.__output_doc(field)
152
153         directive = util.Directive('java:field', signature.build())
154         directive.add_content(doc)
155
156         return directive
157
158     def compile_constructor(self, constructor):
159         signature = util.StringBuilder()
160
161         for annotation in constructor.annotations:
162             formatter.output_annotation(annotation, signature)
163
164         formatter.output_modifiers(constructor.modifiers, signature)
165         signature.append(' ')
166
167         if constructor.type_parameters:
168             formatter.output_type_params(constructor.type_parameters, signature)
169             signature.append(' ')
170
171         signature.append(constructor.name)
172
173         signature.append('(')
174         formatter.output_list(formatter.output_formal_param, constructor.parameters, signature, ', ')
175         signature.append(')')
176
177         if constructor.throws:
178             signature.append(' throws ')
179             formatter.output_list(formatter.output_exception, constructor.throws, signature, ', ')
180
181         doc = self.__output_doc(constructor)
182
183         directive = util.Directive('java:constructor', signature.build())
184         directive.add_content(doc)
185
186         return directive
187
188     def compile_method(self, method):
189         signature = util.StringBuilder()
190
191         for annotation in method.annotations:
192             formatter.output_annotation(annotation, signature)
193
194         formatter.output_modifiers(method.modifiers, signature)
195         signature.append(' ')
196
197         if method.type_parameters:
198             formatter.output_type_params(method.type_parameters, signature)
199             signature.append(' ')
200
201         formatter.output_type(method.return_type, signature)
202         signature.append(' ')
203
204         signature.append(method.name)
205
206         signature.append('(')
207         formatter.output_list(formatter.output_formal_param, method.parameters, signature, ', ')
208         signature.append(')')
209
210         if method.throws:
211             signature.append(' throws ')
212             formatter.output_list(formatter.output_exception, method.throws, signature, ', ')
213
214         doc = self.__output_doc(method)
215
216         directive = util.Directive('java:method', signature.build())
217         directive.add_content(doc)
218
219         return directive
220
221     def compile_type_document(self, imports_block, package, name, declaration):
222         """ Compile a complete document, documenting a type and its members """
223
224         outer_type = name.rpartition('.')[0]
225
226         document = util.Document()
227         document.add(imports_block)
228         document.add_heading(name, '=')
229
230         method_summary = util.StringBuilder()
231         document.add_object(method_summary)
232
233         package_dir = util.Directive('java:package', package)
234         package_dir.add_option('noindex')
235         document.add_object(package_dir)
236
237         # Add type-level documentation
238         type_dir = self.compile_type(declaration)
239         if outer_type:
240             type_dir.add_option('outertype', outer_type)
241         document.add_object(type_dir)
242
243         if isinstance(declaration, javalang.tree.EnumDeclaration):
244             enum_constants = list(declaration.body.constants)
245             enum_constants.sort(key=lambda c: c.name)
246
247             document.add_heading('Enum Constants')
248             for enum_constant in enum_constants:
249                 if self.member_headers:
250                     document.add_heading(enum_constant.name, '^')
251                 c = self.compile_enum_constant(name, enum_constant)
252                 c.add_option('outertype', name)
253                 document.add_object(c)
254
255         fields = list(filter(self.filter, declaration.fields))
256         if fields:
257             document.add_heading('Fields', '-')
258             fields.sort(key=lambda f: f.declarators[0].name)
259             for field in fields:
260                 if self.member_headers:
261                     document.add_heading(field.declarators[0].name, '^')
262                 f = self.compile_field(field)
263                 f.add_option('outertype', name)
264                 document.add_object(f)
265
266         constructors = list(filter(self.filter, declaration.constructors))
267         if constructors:
268             document.add_heading('Constructors', '-')
269             constructors.sort(key=lambda c: c.name)
270             for constructor in constructors:
271                 if self.member_headers:
272                     document.add_heading(constructor.name, '^')
273                 c = self.compile_constructor(constructor)
274                 c.add_option('outertype', name)
275                 document.add_object(c)
276
277         methods = list(filter(self.filter, declaration.methods))
278         if methods:
279             document.add_heading('Methods', '-')
280             methods.sort(key=lambda m: m.name)
281             for method in methods:
282                 if self.member_headers:
283                     document.add_heading(method.name, '^')
284                 m = self.compile_method(method)
285                 m.add_option('outertype', name)
286                 document.add_object(m)
287
288         return document
289
290     def compile(self, ast):
291         """ Compile autodocs for the given Java syntax tree. Documents will be
292         returned documenting each separate type. """
293
294         documents = {}
295
296         imports = util.StringBuilder()
297         for imp in ast.imports:
298             if imp.static or imp.wildcard:
299                 continue
300
301             package_parts = []
302             cls_parts = []
303
304             for part in imp.path.split('.'):
305                 if cls_parts or part[0].isupper():
306                     cls_parts.append(part)
307                 else:
308                     package_parts.append(part)
309
310
311             # If the import's final part wasn't capitalized,
312             # append it to the class parts anyway so sphinx doesn't complain.
313             if cls_parts == []:
314                 cls_parts.append(package_parts.pop())
315
316             package = '.'.join(package_parts)
317             cls = '.'.join(cls_parts)
318
319             imports.append(util.Directive('java:import', package + ' ' + cls).build())
320         import_block = imports.build()
321
322         if not ast.package:
323             raise ValueError('File must have package declaration')
324
325         package = ast.package.name
326         type_declarations = []
327         for path, node in ast.filter(javalang.tree.TypeDeclaration):
328             if not self.filter(node):
329                 continue
330
331             classes = [n.name for n in path if isinstance(n, javalang.tree.TypeDeclaration)]
332             classes.append(node.name)
333
334             name = '.'.join(classes)
335             type_declarations.append((package, name, node))
336
337         for package, name, declaration in type_declarations:
338             full_name = package + '.' + name
339             document = self.compile_type_document(import_block, package, name, declaration)
340             documents[full_name] = (package, name, document.build())
341         return documents
342
343     def compile_docblock(self, documented):
344         ''' Compiles a single, standalone docblock. '''
345         return self.__output_doc(documented).build()