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