File: Synopsis/Formatters/DocBook/Markup/Javadoc.py 1
2
3
4
5
6
7
8from Synopsis.Formatters.DocBook.Markup import *
9import re
10
11def attributes(keys):
12 "Convert a name/value dict to a string of attributes"
13
14 return ''.join(['%s="%s"'%(k,v) for k,v in keys.items()])
15
16def element(_type, body, **keys):
17 "Wrap the body in a tag of given type and attributes"
18
19 return '<%s %s>%s</%s>'%(_type,attributes(keys),body,_type)
20
21def title(name) : return element('title', name)
22def para(body) : return element('para', body)
23def listitem(body) : return element('listitem', body)
24def term(body) : return element('term', body)
25def link(linkend, label) : return element('link', label, linkend=linkend)
26class Javadoc(Formatter):
27 """
28 A formatter that formats comments similar to Javadoc.
29 See `Javadoc Spec`_ for info.
30
31 .. _Javadoc Spec: http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/javadoc.html"""
32
33 class Block:
34
35 def __init__(self, tag, arg, body):
36 self.tag, self.arg, self.body = tag, arg, body
37
38
39 summary = r'(\s*[\w\W]*?\.)(\s|$)'
40 block_tag = r'(^\s*\@\w+[\s$])'
41 inline_tag = r'{@(?P<tag>\w+)\s+(?P<content>[^}]+)}'
42 inline_tag_split = r'({@\w+\s+[^}]+})'
43 xref = r'([\w#.]+)(?:\([^\)]*\))?\s*(.*)'
44
45 tag_name = {
46 'author': ['Author', 'Authors'],
47 'date': ['Date', 'Dates'],
48 'deprecated': ['Deprecated', 'Deprecated'],
49 'exception': ['Exception', 'Exceptions'],
50 'invariant': ['Invariant', 'Invariants'],
51 'keyword': ['Keyword', 'Keywords'],
52 'param': ['Parameter', 'Parameters'],
53 'postcondition': ['Postcondition', 'Postcondition'],
54 'precondition': ['Precondition', 'Preconditions'],
55 'return': ['Returns', 'Returns'],
56 'see': ['See also', 'See also'],
57 'throws': ['Throws', 'Throws'],
58 'version': ['Version', 'Versions']}
59 arg_tags = ['param', 'keyword', 'exception']
60
61
62 def __init__(self):
63 """Create regex objects for regexps"""
64
65 self.summary = re.compile(Javadoc.summary)
66 self.block_tag = re.compile(Javadoc.block_tag, re.M)
67 self.inline_tag = re.compile(Javadoc.inline_tag)
68 self.inline_tag_split = re.compile(Javadoc.inline_tag_split)
69 self.xref = re.compile(Javadoc.xref)
70
71
72 def split(self, doc):
73 """Split a javadoc comment into description and blocks."""
74
75 chunks = self.block_tag.split(doc)
76 description = chunks[0]
77 blocks = []
78 for i in range(1, len(chunks)):
79 if i % 2 == 1:
80 tag = chunks[i].strip()[1:]
81 else:
82 if tag in self.arg_tags:
83 arg, body = chunks[i].strip().split(None, 1)
84 else:
85 arg, body = None, chunks[i]
86
87 if tag == 'see' and body:
88 if body[0] in ['"', "'"]:
89 if body[-1] == body[0]:
90 body = body[1:-1]
91 elif body[0] == '<':
92 pass
93 else:
94
95
96 body = '{@link %s}'%body
97 blocks.append(Javadoc.Block(tag, arg, body))
98
99 return description, blocks
100
101
102 def extract_summary(self, description):
103 """Generate a summary from the given description."""
104
105 m = self.summary.match(description)
106 if m:
107 return '<para>%s</para>\n'%m.group(1)
108 else:
109 return '<para>%s</para>\n'%(description.split('\n', 1)[0]+'...')
110
111
112 def format(self, decl):
113 """Format using javadoc markup."""
114
115 doc = decl.annotations.get('doc')
116 doc = doc and doc.text or ''
117 if not doc:
118 return Struct('', '')
119 description, blocks = self.split(doc)
120
121 details = self.format_description(description, decl)
122 summary = self.extract_summary(details)
123 details += self.format_params(blocks, decl)
124 details += self.format_variablelist('return', blocks, decl)
125 details += self.format_throws(blocks, decl)
126 vl = self.format_varlistentry('precondition', blocks, decl)
127 vl += self.format_varlistentry('postcondition', blocks, decl)
128 vl += self.format_varlistentry('invariant', blocks, decl)
129 vl += self.format_varlistentry('author', blocks, decl)
130 vl += self.format_varlistentry('date', blocks, decl)
131 vl += self.format_varlistentry('version', blocks, decl)
132 vl += self.format_varlistentry('deprecated', blocks, decl)
133 vl += self.format_varlistentry('see', blocks, decl)
134 if vl:
135 details += '<variablelist>\n%s\n</variablelist>\n'%vl
136
137 return Struct(summary, details)
138
139
140 def format_description(self, text, decl):
141
142 return '<para>%s</para>\n'%self.format_inlines(decl, text)
143
144
145 def format_inlines(self, decl, text):
146 """Formats inline tags in the text."""
147
148 chunks = self.inline_tag_split.split(text)
149 text = ''
150
151
152 for i in range(len(chunks)):
153 if i % 2 == 0:
154 text += chunks[i]
155 else:
156 m = self.inline_tag.match(chunks[i])
157 if m:
158 text += self.format_inline_tag(m.group('tag'),
159 m.group('content'),
160 decl)
161 return text
162
163
164 def format_params(self, blocks, decl):
165 """Formats a list of (param, description) tags"""
166
167 content = ''
168 params = [b for b in blocks if b.tag == 'param']
169 if params:
170 params = [element('varlistentry', term(p.arg) + listitem(para(p.body)))
171 for p in params]
172 content += element('variablelist',
173 title('Parameters') + '\n' + '\n'.join(params))
174 kwds = [b for b in blocks if b.tag == 'keyword']
175 if kwds:
176 kwds = [element('varlistentry', term(k.arg) + listitem(para(k.body)))
177 for k in kwds]
178 content += element('variablelist',
179 title('Keywords') + '\n' + '\n'.join(kwds))
180 return content
181
182
183 def format_throws(self, blocks, decl):
184
185 content = ''
186 throws = [b for b in blocks if b.tag in ['throws', 'exception']]
187 if throws:
188 throws = [element('varlistentry', term(t.arg) + listitem(para(t.body)))
189 for t in throws]
190 content += element('variablelist',
191 title('Throws') + '\n' + '\n'.join(throws))
192 return content
193
194
195 def format_variablelist(self, tag, blocks, decl):
196 """
197 Generate a variablelist for the given tag.
198 Each matching block is formatted to a varlistentry, with the value
199 of its 'arg' member becoming the term."""
200
201 content = ''
202 items = [b for b in blocks if b.tag == tag]
203 if items:
204 items = [element('varlistentry', term(i.arg) + listitem(para(self.format_inlines(decl, i.body))))
205 for i in items]
206 content += element('variablelist',
207 title(self.tag_name[tag][1]) + '\n' + '\n'.join(items))
208 return content
209
210
211 def format_varlistentry(self, tag, blocks, decl):
212 """
213 Generate a varlistentry for the given tag.
214 The tag value itself becomes the term. If multiple blocks match,
215 format them as an (inlined) simplelist, otherwise as a para."""
216
217 items = [b for b in blocks if b.tag == tag]
218 if not items:
219 return ''
220 if len(items) > 1:
221 items = [element('member', self.format_inlines(decl, i.body)) for i in items]
222 content = element('simplelist', '\n'.join(items), type='inline') + '\n'
223 else:
224 content = element('para', self.format_inlines(decl, items[0].body))
225
226 return element('varlistentry', term(self.tag_name[tag][1]) + '\n' + listitem(content))
227
228
229 def format_inline_tag(self, tag, content, decl):
230
231 text = ''
232 if tag == 'link':
233 xref = self.xref.match(content)
234 name, label = xref.groups()
235 if not label:
236 label = name
237
238
239
240 if '#' in name:
241 if '::' in name:
242 name = name.replace('#', '::')
243 else:
244 name = name.replace('#', '.')
245 target = self.lookup_symbol(name, decl.name[:-1])
246 if target:
247 text += link(target, label)
248 else:
249 text += label
250 elif tag == 'code':
251 text += '<code>%s</code>'%escape(content)
252 elif tag == 'literal':
253 text += '<literal>%s</literal>'%escape(content)
254
255 return text
256
Generated on Thu Apr 16 16:27:15 2009 by
synopsis (version devel)