1 //
2 // Copyright (c) 2007, Brian Frank and Andy Frank
3 // Licensed under the Academic Free License version 3.0
4 //
5 // History:
6 // 5 May 07 Brian Frank Creation
7 //
8
9 using compiler
10 using fandoc
11
12 **
13 ** ApiToHtmlGenerator generates an HTML file for a Type's API
14 **
15 class ApiToHtmlGenerator : HtmlGenerator
16 {
17
18 //////////////////////////////////////////////////////////////////////////
19 // Constructor
20 //////////////////////////////////////////////////////////////////////////
21
22 new make(DocCompiler compiler, Location loc, OutStream out, Type t)
23 : super(compiler, loc, out)
24 {
25 this.t = t
26 sorter := |Slot a, Slot b -> Int| { return a.name <=> b.name }
27 filter := |Slot s -> Bool| { return showSlot(t, s) }
28 this.slots = t.slots.rw.sort(sorter).findAll(filter)
29 }
30
31 //////////////////////////////////////////////////////////////////////////
32 // Generator
33 //////////////////////////////////////////////////////////////////////////
34
35 override Str title()
36 {
37 return t.qname
38 }
39
override Void header()
41 {
42 out.print("<ul>\n")
43 out.print(" <li><a href='../index.html'>$docHome</a></li>\n")
44 out.print(" <li><a href='index.html'>$t.pod.name</a></li>\n")
45 out.print(" <li>$t.name</li>\n")
46 out.print("</ul>\n")
47 }
48
49 override Void content()
50 {
51 out.print("<div class='type'>\n")
52 typeOverview
53 typeDetail
54 out.print("</div>\n")
55 slotsDetail
56 }
57
override Void sidebar()
59 {
60 actions
61 slotsOverview
62 }
63
64 //////////////////////////////////////////////////////////////////////////
65 // Methods
66 //////////////////////////////////////////////////////////////////////////
67
68 **
69 ** Generate the actions.
70 **
71 Void actions()
72 {
73 out.print("<h2>More Info</h2>\n")
74 out.print("<ul>\n")
75 out.print(" <li><a href='${t.name}_src.html'>View Source</a></li>\n")
76 out.print(" <li><a href='#' onclick='ShowSlots.toggle(event); return false;'>")
77 out.print("Show All Slots</a></li>\n")
78 out.print("</ul>\n")
79 }
80
81 **
82 ** Generate the type overview documentation.
83 **
84 Void typeOverview()
85 {
86 out.print("<div class='overview'>\n")
87 out.print("<h2>")
88 if (t.isConst) out.print("const ")
89 if (t.isMixin) out.print("mixin")
90 else
91 {
92 if (t.isAbstract) out.print("abstract ")
93 if (t.isEnum)
94 out.print("enum")
95 else
96 out.print("class")
97 }
98 out.print("</h2>\n")
99 out.print("<h1>$t.qname</h1>\n")
100 if (t.base != null) inheritance
101 out.print("</div>\n")
102 }
103
104 **
105 ** Generate the type detail documentation.
106 **
107 Void typeDetail()
108 {
109 if (t.doc == null) return
110 out.print("<div class='detail'>\n")
111 fandoc(t.doc)
112 out.print("</div>\n")
113 }
114
115 **
116 ** Generate the type inheritance.
117 **
118 Void inheritance()
119 {
120 chain := Type[t]
121 base := t.base
122 while (base != null)
123 {
124 chain.insert(0, base)
125 base = base.base
126 }
127 out.print("<pre>")
128 chain.each |Type t, Int i|
129 {
130 if (i > 0) out.print("\n${Str.spaces(i*2)}")
131 if (i == chain.size-1) out.print(t.qname)
132 else out.print("<a href='${compiler.uriMapper.map(t.qname, loc)}'>$t.qname</a>")
133 }
134 t.mixins.each |Type t, Int i|
135 {
136 //if (i == 0) out.print("\n\nMixin: ")
137 if (i == 0) out.print(" : ")
138 else out.print(", ")
139 out.print("<a href='${compiler.uriMapper.map(t.qname, loc)}'>$t.qname</a>")
140 }
141 out.print("</pre>")
142 }
143
144 **
145 ** Generate the slot overview documentation.
146 **
147 Void slotsOverview(Bool hideByDefault := true)
148 {
149 out.print("<div class='slots'>\n")
150 out.print("<div class='overview'>\n")
151 out.print("<h2>Slots</h2>\n")
152 out.print("<ul>\n")
153 slots.each |Slot slot|
154 {
155 if (!showSlot(t, slot)) return
156 out.print(" <li")
157 if (!showByDefault(t, slot)) out.print(" class='hidden'")
158 if (!hideByDefault) out.print(" style='display: block;'")
159 out.print("><a href='#$slot.name'>$slot.name</a></li>\n")
160 }
161 out.print("</ul>\n")
162 out.print("</div>\n")
163 out.print("</div>\n")
164 }
165
166 **
167 ** Generate the slot detail documentation.
168 **
169 Void slotsDetail()
170 {
171 out.print("<div class='slots'>\n")
172 out.print("<div class='detail'>\n")
173 out.print("<h2>Slots</h2>\n")
174 out.print("<dl>\n")
175 slots.each |Slot slot| { slotDetail(slot) }
176 out.print("</dl>\n")
177 out.print("</div>\n")
178 out.print("</div>\n")
179 }
180
181 **
182 ** Generate the documentation for the given slot.
183 **
184 Void slotDetail(Slot slot)
185 {
186 if (!showSlot(t, slot)) return
187
188 oldfile := loc.file
189 loc.file = slot.qname
190
191 hidden := !showByDefault(t, slot)
192 cls := (slot.isField) ? "field" : "method"
193 if (hidden) cls += " hidden"
194 out.print("<dt id='$slot.name' class='$cls'>$slot.name</dt>\n")
195 out.print("<dd")
196 if (hidden) out.print(" class='hidden'")
197 out.print(">\n")
198
199 // Slot spec
200 out.print("<p><code>")
201 if (slot.isField)
202 {
203 f := (Field)slot
204 slotModifiers(f)
205 typeLink(f.of)
206 out.print(" $f.name")
207 setter(f)
208 }
209 else
210 {
211 m := (Method)slot
212 if (m.isCtor) out.print("new")
213 else
214 {
215 slotModifiers(m)
216 typeLink(m.returns)
217 }
218 out.print(" $m.name")
219 out.print("(")
220 m.params.each |Param p, Int i|
221 {
222 if (i > 0) out.print(", ")
223 typeLink(p.of)
224 out.print(" $p.name")
225 if (p.hasDefault) out.print(" := def")
226 }
227 out.print(")")
228 }
229 out.print("</code></p>\n")
230
231 // inherited
232 if (isInnherited(t, slot))
233 {
234 out.print("<p>Inherited from ")
235 typeLink(slot.parent)
236 out.print("</p>\n")
237 }
238
239 // Slot comment
240 if (slot.doc != null)
241 fandoc(slot.doc)
242
243 out.print("</dd>\n")
244
245 loc.file = oldfile
246 }
247
248 **
249 ** Write a slot's modifiers.
250 **
251 Void slotModifiers(Slot s)
252 {
253 if (s.isVirtual)
254 {
255 if (s.isAbstract) out.print("abstract ")
256 else if (s.isOverride) out.print("override ")
257 else out.print("virtual ")
258 }
259
260 if (s.isStatic) out.print("static ")
261 else if (s.isConst) out.print("const ")
262
263 if (s.isProtected) out.print("protected ")
264 else if (s.isPrivate) out.print("private ")
265 else if (s.isInternal) out.print("internal ")
266
267 if (s.isField)
268 {
269 Method z := s->setter
270 if (z != null && !s.isPrivate && z.isPrivate) out.print("readonly ")
271 }
272 }
273
274 **
275 ** Write a field's setter proctection level if its different
276 ** from the getter's level.
277 **
278 Void setter(Field f)
279 {
280 Method s := f->setter
281 if (s == null) return
282 if (f.isPublic && s.isPublic) return
283 if (f.isProtected && s.isProtected) return
284 if (f.isPrivate && s.isPrivate) return
285 if (f.isInternal && s.isInternal) return
286
287 // this case handled already in slotModifers() by writing 'readonly'
288 if (!f.isPrivate && s.isPrivate) return
289
290 // if we made this far, they must be different
291 out.print(" { ")
292 slotModifiers(s)
293 out.print("set }")
294 }
295
296 **
297 ** Write a type link out in the form <a href='type.uri'>type.name</a>.
298 **
299 Void typeLink(Type t)
300 {
301 display := typeToDisplay(t)
302
303 // TODO - not really sure what the right thing to
304 // do here is - these are the generic types, like
305 // sys::A, sys::V, etc
306 if (t.pod.name == "sys" && t.name.size == 1)
307 t = Obj.type
308
309 out.print("<a href='${compiler.uriMapper.map(t.qname, loc)}'>$display</a>")
310 }
311
312 **
313 ** Convert the Type name to a display string by stripping
314 ** the pod names from the signature.
315 **
316 static Str typeToDisplay(Type t)
317 {
318 if (!t.isGeneric)
319 {
320 p := t.params
321 if (p["L"] != null) return typeToDisplay(p["V"]) + "[]"
322 if (p["M"] != null) return typeToDisplay(p["K"]) + ":" + typeToDisplay(p["V"])
323 if (p["R"] != null)
324 {
325 buf := StrBuf.make.addChar('|')
326
327 keys := p.keys.rw.sort |Str a, Str b -> Int| { return a <=> b }
328 keys.each |Str k, Int i|
329 {
330 if (k == "R") return
331 if (i > 0) buf.add(", ")
332 buf.add(typeToDisplay(p[k]))
333 }
334
335 if (p["R"] != Void.type)
336 buf.add(" -> ").add(typeToDisplay(p["R"]))
337
338 buf.addChar('|')
339 return buf.toStr
340 }
341 }
342
343 // Force sys::A,B,C,etc to be Obj
344 //if (t.pod.name == "sys" && t.name.size == 1) t = Obj.type
345 return t.name
346 }
347
348 **
349 ** Write out the fandoc for this text - if an exception
350 ** is thrown, write the original text.
351 **
352 Void fandoc(Str text)
353 {
354 try
355 {
356 doc := FandocParser.make.parse("API for $t", InStream.makeForStr(text))
357 doc.children.each |DocNode child| { child.write(this) }
358 }
359 catch { out.print("<p>$text<p>\n") }
360 }
361
362 //////////////////////////////////////////////////////////////////////////
363 // Fields
364 //////////////////////////////////////////////////////////////////////////
365
366 Type t // type to documenting
367 Slot[] slots // slots to document
368
369 }