1<xsl:stylesheet version="3.0"
2  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
3  xmlns:xs="http://www.w3.org/2001/XMLSchema"
4  xmlns:d="http://github.com/vinniefalco/docca"
5  exclude-result-prefixes="xs d"
6  expand-text="yes">
7
8  <!-- TODO: make sure this doesn't screw up any formatting -->
9  <!-- NEW TODO: verify we don't need/want this -->
10  <!--
11  <xsl:output indent="yes"/>
12  -->
13
14  <xsl:include href="common.xsl"/>
15
16  <xsl:key name="visible-memberdefs-by-id"
17           match="memberdef[$include-private-members or not(@prot eq 'private')]"
18           use="@id"/>
19
20  <xsl:key name="elements-by-refid" match="compound | member" use="@refid"/>
21
22  <xsl:variable name="index-xml" select="/"/>
23
24  <xsl:template match="/">
25    <index>
26      <xsl:apply-templates select="/doxygenindex/compound"/>
27    </index>
28    <!-- Testing the ID-related functions
29    <xsl:value-of select="replace(d:extract-ns('put'), '::$', '')"/>
30    <xsl:text>&#xA;</xsl:text>
31    <xsl:value-of select="replace(d:extract-ns('foobar::parser::put'), '::$', '')"/>
32    <xsl:text>&#xA;</xsl:text>
33    <xsl:value-of select="d:extract-ns('foobar::parser::put&lt;foo::bar, bat::bang>')"/>
34    <xsl:text>&#xA;</xsl:text>
35    <xsl:value-of select="d:strip-ns('boost::beast::http::parser::basic_parser&lt; foo::isRequest, bar::parser &gt;')"/>
36    <xsl:text>&#xA;</xsl:text>
37    <xsl:value-of select="d:strip-doc-ns('boost::beast::http::parser::basic_parser&lt; foo::isRequest, bar::parser &gt;')"/>
38    <xsl:text>&#xA;</xsl:text>
39    <xsl:text>&#xA;</xsl:text>
40    <xsl:value-of select="d:make-id('boost::beast::http::parser::basic_parser&lt; foo::isRequest, bar::parser &gt;')"/>
41    -->
42  </xsl:template>
43
44  <!-- Default implementation; can be customized/overridden -->
45  <xsl:function name="d:should-ignore-compound">
46    <xsl:param name="compound" as="element(compound)"/>
47    <xsl:sequence select="false()"/>
48  </xsl:function>
49
50  <xsl:template match="compound[d:should-ignore-compound(.)]"/>
51  <xsl:template match="compound">
52    <!-- Load each input file only once -->
53    <xsl:variable name="source-doc" select="d:get-source-doc(.)"/>
54    <!-- Ignore private classes unless private members are enabled -->
55    <xsl:if test="$include-private-members or not($source-doc/doxygen/compounddef/@prot eq 'private')">
56      <!-- Look up memberdefs (and constrain by visibility) only once -->
57      <xsl:variable name="memberdefs" select="key('visible-memberdefs-by-id', member/@refid, $source-doc)"/>
58      <!-- Create a filtered copy of members within their minimal context, listing only the visible ones -->
59      <xsl:variable name="visible-members" as="element(member)*">
60        <xsl:variable name="compound" as="element()">
61          <compound kind="{@kind}" refid="{@refid}">
62            <name>{name}</name>
63            <xsl:copy-of select="member[@refid = $memberdefs/@id]"/>
64          </compound>
65        </xsl:variable>
66        <xsl:sequence select="$compound/member"/>
67      </xsl:variable>
68      <xsl:apply-templates mode="create-page" select=".">
69        <xsl:with-param name="source-doc" select="$source-doc" tunnel="yes"/>
70        <xsl:with-param name="memberdefs" select="$memberdefs" tunnel="yes"/>
71        <xsl:with-param name="visible-members" select="$visible-members" tunnel="yes"/>
72      </xsl:apply-templates>
73    </xsl:if>
74  </xsl:template>
75
76          <xsl:function name="d:get-source-doc" as="document-node()">
77            <xsl:param name="compound" as="element(compound)"/>
78            <xsl:sequence select="document($compound/@refid||'.xml', $index-xml)"/>
79          </xsl:function>
80
81  <!-- Split up the content into class, struct, and member pages -->
82  <xsl:template mode="create-page" match="*"/>
83  <xsl:template mode="create-page" match="compound[@kind = 'namespace']">
84    <xsl:apply-templates mode="child-pages" select="."/>
85  </xsl:template>
86  <xsl:template mode="create-page" match="compound[@kind = ('class','struct')]
87                                        | compound/member">
88    <xsl:variable name="page-id" as="xs:string">
89      <xsl:apply-templates mode="page-id" select="."/>
90    </xsl:variable>
91    <page id="{$page-id}" href="{$page-id}.xml">
92      <xsl:result-document href="xml-pages/{$page-id}.xml">
93        <xsl:apply-templates mode="page-content" select=".">
94          <xsl:with-param name="page-id" select="$page-id" tunnel="yes"/>
95        </xsl:apply-templates>
96      </xsl:result-document>
97      <xsl:apply-templates mode="child-pages" select="."/>
98    </page>
99  </xsl:template>
100
101          <!-- Create the member page for each child (or, if overloaded, the overload-list page) -->
102          <xsl:template mode="child-pages" match="compound">
103            <xsl:param name="visible-members" tunnel="yes"/>
104            <!-- Create a page for each unique member name -->
105            <xsl:for-each select="$visible-members[not(name = preceding-sibling::member/name)]">
106              <xsl:apply-templates mode="create-page" select=".">
107                <xsl:with-param name="is-overload-list-page" select="d:is-overloaded(.)" tunnel="yes"/>
108              </xsl:apply-templates>
109            </xsl:for-each>
110          </xsl:template>
111
112          <!-- A member page doesn't have children, unless it is an overload-list page -->
113          <xsl:template mode="child-pages" match="compound/member">
114            <xsl:param name="is-overload-list-page" tunnel="yes"/>
115            <xsl:if test="$is-overload-list-page">
116              <xsl:apply-templates mode="create-page" select="d:overloaded-members(.)">
117                <xsl:with-param name="is-overload-list-page" select="false()" tunnel="yes"/>
118              </xsl:apply-templates>
119            </xsl:if>
120          </xsl:template>
121
122
123          <xsl:template mode="page-id" match="compound">{d:make-id(name)}</xsl:template>
124          <xsl:template mode="page-id" match="member">
125            <xsl:param name="is-overload-list-page" tunnel="yes"/>
126            <xsl:value-of>
127              <xsl:apply-templates mode="base-member-page-id" select="."/>
128              <!-- Append the overload-specific suffix, if applicable -->
129              <xsl:if test="d:is-overloaded(.) and not($is-overload-list-page)">
130                <xsl:value-of select="d:make-id('.overload'||d:overload-position(.))"/>
131              </xsl:if>
132            </xsl:value-of>
133          </xsl:template>
134
135                  <xsl:function name="d:is-overloaded" as="xs:boolean">
136                    <xsl:param name="member" as="element(member)"/>
137                    <xsl:sequence select="exists(d:overloaded-members($member)[2])"/>
138                  </xsl:function>
139
140                  <xsl:function name="d:overload-position" as="xs:integer">
141                    <xsl:param name="member" as="element(member)"/>
142                    <xsl:sequence select="1 + count($member/preceding-sibling::member[name eq $member/name])"/>
143                  </xsl:function>
144
145                  <xsl:function name="d:overloaded-members" as="element(member)+">
146                    <xsl:param name="member" as="element(member)"/>
147                    <xsl:sequence select="$member/../member[name eq $member/name]"/>
148                  </xsl:function>
149
150
151          <xsl:template mode="base-member-page-id" priority="1"
152                                                   match="compound[@kind eq 'namespace']
153                                                                  /member">{d:make-id(../name||'::'||name)}</xsl:template>
154          <xsl:template mode="base-member-page-id" match="compound/member">{d:make-id(../name||'.' ||name)}</xsl:template>
155
156
157          <!-- The content for a class or struct is the original source document, pared down some -->
158          <xsl:template mode="page-content" match="compound">
159            <xsl:param name="source-doc" tunnel="yes"/>
160            <xsl:apply-templates mode="compound-page" select="$source-doc"/>
161          </xsl:template>
162
163                  <!-- By default, copy everything -->
164                  <xsl:template mode="compound-page" match="@* | node()" name="copy-in-compound-page">
165                    <xsl:copy>
166                      <xsl:apply-templates mode="#current" select="@*"/>
167                      <xsl:apply-templates mode="compound-page-insert" select="."/>
168                      <xsl:apply-templates mode="#current"/>
169                    </xsl:copy>
170                  </xsl:template>
171
172                          <!-- By default, don't insert anything -->
173                          <xsl:template mode="compound-page-insert" match="*"/>
174
175                  <xsl:template mode="compound-page" match="listofallmembers"/>
176
177                  <xsl:template mode="compound-page" match="memberdef/@*"/>
178
179                  <!-- But directly inside <memberdef>, don't copy anything... -->
180                  <xsl:template mode="compound-page" match="memberdef/node()"/>
181
182                  <!-- ...except for <name>, <briefdescription>, and <type> -->
183                  <xsl:template mode="compound-page" match="memberdef/name
184                                                          | memberdef/briefdescription
185                                                          | memberdef/type" priority="1">
186                    <xsl:call-template name="copy-in-compound-page"/>
187                  </xsl:template>
188
189                  <!-- Insert a reference to each child member's page ID -->
190                  <xsl:template mode="compound-page-insert" match="memberdef">
191                    <xsl:attribute name="d:page-refid" select="d:make-id(/doxygen/compounddef/compoundname||'.'||name)"/>
192                  </xsl:template>
193
194                  <!-- Alternative implementation in case we need to start controlling whitespace more
195                  <xsl:template mode="compound-page" match="memberdef">
196                    <memberdef>
197                      <xsl:text>&#xA;</xsl:text>
198                      <xsl:copy-of select="name"/>
199                      <xsl:text>&#xA;</xsl:text>
200                      <xsl:copy-of select="briefdescription"/>
201                    </memberdef>
202                  </xsl:template>
203                  -->
204
205          <!-- The content for a member page is a subset of the source document -->
206          <xsl:template mode="page-content" match="compound/member">
207            <xsl:param name="is-overload-list-page" tunnel="yes"/>
208            <xsl:choose>
209              <xsl:when test="$is-overload-list-page">
210                <!-- For the overload list page, include the content for every like-named member -->
211                <xsl:apply-templates mode="list-page" select=".">
212                  <xsl:with-param name="applicable-members" select="d:overloaded-members(.)" tunnel="yes"/>
213                </xsl:apply-templates>
214              </xsl:when>
215              <xsl:otherwise>
216                <!-- Otherwise, this page is just for one implementation (whether overloaded or not) -->
217                <xsl:apply-templates mode="member-page" select="."/>
218              </xsl:otherwise>
219            </xsl:choose>
220          </xsl:template>
221
222                  <xsl:template mode="list-page member-page" match="member" priority="2">
223                    <xsl:param name="applicable-members" as="element(member)+" select="." tunnel="yes"/>
224                    <xsl:param name="source-doc" tunnel="yes"/>
225                    <xsl:param name="memberdefs" tunnel="yes"/>
226                    <xsl:apply-templates mode="#current" select="$source-doc">
227                      <xsl:with-param name="target-memberdefs"
228                                      select="$memberdefs[@id = $applicable-members/@refid]"
229                                      tunnel="yes"/>
230                      <xsl:with-param name="member" select="." tunnel="yes"/>
231                    </xsl:apply-templates>
232                  </xsl:template>
233
234                  <!-- Always copy the name of the parent compound -->
235                  <xsl:template mode="list-page member-page" match="compoundname" priority="2">
236                    <xsl:copy-of select="."/>
237                  </xsl:template>
238
239                  <!-- Otherwise, only copy an element if it's the target member or one of its ancestors -->
240                  <xsl:template mode="list-page member-page" match="*" priority="1">
241                    <xsl:param name="target-memberdefs" tunnel="yes"/>
242                    <xsl:if test=". intersect $target-memberdefs/ancestor-or-self::*">
243                      <xsl:next-match/>
244                    </xsl:if>
245                  </xsl:template>
246
247                  <!-- By default, copy everything -->
248                  <xsl:template mode="list-page" match="@* | node()">
249                    <xsl:copy>
250                      <xsl:apply-templates mode="#current" select="@*"/>
251                      <xsl:apply-templates mode="list-page-insert" select="."/>
252                      <xsl:apply-templates mode="#current"/>
253                    </xsl:copy>
254                  </xsl:template>
255
256                          <!-- By default, don't insert anything -->
257                          <xsl:template mode="list-page-insert" match="*"/>
258
259
260                  <!-- By default, copy everything -->
261                  <xsl:template mode="member-page
262                                      copy-member-content" match="@* | node()">
263                    <xsl:copy>
264                      <xsl:apply-templates mode="#current" select="@*"/>
265                      <xsl:apply-templates mode="member-page-insert" select="."/>
266                      <xsl:apply-templates mode="#current"/>
267                      <xsl:apply-templates mode="member-page-append" select="."/>
268                    </xsl:copy>
269                  </xsl:template>
270
271                          <!-- By default, don't insert or append anything -->
272                          <xsl:template mode="member-page-insert
273                                              member-page-append" match="*"/>
274
275                  <!-- Strip out extraneous whitespace -->
276                  <xsl:template mode="list-page member-page" match="compounddef/text() | sectiondef/text()"/>
277
278                  <!-- Switch to an unfiltered copy once we're done filtering out the undesired elements -->
279                  <xsl:template mode="list-page member-page" match="memberdef/node()" priority="2">
280                    <xsl:apply-templates mode="copy-member-content" select="."/>
281                  </xsl:template>
282
283                  <!-- Add the page ID to the top of all page types -->
284                  <xsl:template mode="compound-page-insert
285                                      member-page-insert
286                                      list-page-insert" match="/doxygen" priority="2">
287                    <xsl:param name="page-id" tunnel="yes"/>
288                    <xsl:attribute name="d:page-id" select="$page-id"/>
289                    <xsl:next-match/>
290                  </xsl:template>
291
292                  <!-- Also, if applicable, insert the overload position and/or base compound reference of this member -->
293                  <xsl:template mode="member-page-insert" match="/doxygen" priority="1">
294                    <xsl:param name="member" tunnel="yes"/>
295                    <xsl:if test="d:is-overloaded($member)">
296                      <xsl:attribute name="d:overload-position" select="d:overload-position($member)"/>
297                      <xsl:attribute name="d:overload-size" select="count(d:overloaded-members($member))"/>
298                    </xsl:if>
299                    <xsl:if test="$member[not(starts-with(@refid, ../@refid))]">
300                      <xsl:variable name="base-compound" select="$index-xml/*/compound[starts-with($member/@refid, @refid)]
301                                                                                      [not(d:should-ignore-compound(.))]"/>
302                      <xsl:apply-templates mode="base-compound-atts" select="$base-compound"/>
303                    </xsl:if>
304                    <xsl:next-match/>
305                  </xsl:template>
306
307                          <xsl:template mode="base-compound-atts" match="compound">
308                            <xsl:attribute name="d:base-compound-name" select="d:strip-doc-ns(name)"/>
309                            <xsl:attribute name="d:base-compound-refid">
310                              <xsl:apply-templates mode="page-id" select="."/>
311                            </xsl:attribute>
312                          </xsl:template>
313
314                  <!-- Make data available for the typedef tables, if applicable -->
315                  <xsl:template mode="member-page-append" match="memberdef[@kind eq 'typedef']
316                                                                          [type/ref]
317                                                                          [not(contains(type, '*'))]">
318                    <xsl:for-each select="type/ref">
319                      <d:referenced-typedef-class>
320                        <xsl:variable name="compound" select="d:get-target-element(.)[self::compound]"/>
321                        <xsl:apply-templates mode="compound-page" select="$compound ! d:get-source-doc(.)/*/compounddef"/>
322                      </d:referenced-typedef-class>
323                    </xsl:for-each>
324                  </xsl:template>
325
326                  <!-- Finally, add the page type -->
327                  <xsl:template mode="compound-page-insert" match="/doxygen">
328                    <xsl:attribute name="d:page-type" select="'compound'"/>
329                  </xsl:template>
330                  <xsl:template mode="member-page-insert" match="/doxygen">
331                    <xsl:attribute name="d:page-type" select="'member'"/>
332                  </xsl:template>
333                  <xsl:template mode="list-page-insert" match="/doxygen">
334                    <xsl:attribute name="d:page-type" select="'overload-list'"/>
335                  </xsl:template>
336
337                  <!-- For overload-list pages, include the page id for each member -->
338                  <xsl:template mode="list-page-insert" match="memberdef">
339                    <xsl:param name="applicable-members" tunnel="yes"/>
340                    <xsl:variable name="this-id" select="@id"/>
341                    <xsl:variable name="original-member" select="$applicable-members[@refid eq $this-id]"/>
342                    <xsl:attribute name="d:page-refid">
343                      <xsl:apply-templates mode="page-id" select="$original-member">
344                        <xsl:with-param name="is-overload-list-page" select="false()" tunnel="yes"/>
345                      </xsl:apply-templates>
346                    </xsl:attribute>
347                  </xsl:template>
348
349                  <!-- For public innerclasses, insert the referenced class inline -->
350                  <xsl:template mode="compound-page-insert" match="innerclass[@prot eq 'public']">
351                    <xsl:attribute name="d:page-refid" select="d:make-id(.)"/>
352                    <d:referenced-inner-class>
353                      <xsl:variable name="compound" select="d:get-target-element(.)" as="element(compound)"/>
354                      <xsl:apply-templates mode="compound-page" select="d:get-source-doc($compound)/*/compounddef"/>
355                    </d:referenced-inner-class>
356                  </xsl:template>
357
358                  <!-- Resolve the referenced page IDs for later link generation -->
359                  <xsl:template mode="compound-page-insert member-page-insert" match="ref">
360                    <xsl:attribute name="d:refid">
361                      <xsl:apply-templates mode="page-id" select="d:get-target-element(.)">
362                        <!-- For inline links to member pages, only link to the base page id (no overloads) -->
363                        <xsl:with-param name="is-overload-list-page" select="true()" tunnel="yes"/>
364                      </xsl:apply-templates>
365                    </xsl:attribute>
366                  </xsl:template>
367
368                          <xsl:function name="d:get-target-element" as="element()?"> <!-- to allow for partial builds -->
369                          <!--
370                          <xsl:function name="d:get-target-element" as="element()">
371                          -->
372                            <xsl:param name="ref" as="element()"/> <!-- <ref> or <innerclass> or... -->
373                            <xsl:variable name="referenced-elements" select="key('elements-by-refid', $ref/@refid, $index-xml)"/>
374                            <xsl:variable name="result" as="element()?">
375                              <xsl:choose>
376                                <!-- Handle the case where the referenced element appears two or more times in index.xml -->
377                                <!-- If there's no ambiguity, we're done! -->
378                                <xsl:when test="count($referenced-elements) eq 1">
379                                  <xsl:apply-templates mode="find-target-element" select="$referenced-elements"/>
380                                </xsl:when>
381                                <xsl:otherwise>
382                                  <!-- Otherwise, see if a namespace in the link text successfully disambiguates -->
383                                  <xsl:variable name="qualified-reference" as="element()*">
384                                    <xsl:variable name="parent-in-link-text"
385                                                  select="if (contains($ref,'::'))
386                                                          then d:extract-ns-without-suffix($ref)
387                                                          else ''"/>
388                                    <xsl:sequence select="$referenced-elements[ends-with(parent::compound/name, '::'||$parent-in-link-text)]"/>
389                                  </xsl:variable>
390                                  <xsl:choose>
391                                    <xsl:when test="count($qualified-reference) eq 1">
392                                      <xsl:apply-templates mode="find-target-element" select="$qualified-reference"/>
393                                    </xsl:when>
394                                    <xsl:otherwise>
395                                      <!-- Otherwise, favor the member that's in the same class or namespace as the current page -->
396                                      <xsl:variable name="sibling-reference" as="element()*">
397                                        <xsl:variable name="compound-for-current-page" select="root($ref)/doxygen/compounddef/compoundname/string()"/>
398                                        <xsl:sequence select="$referenced-elements[parent::compound/name eq $compound-for-current-page]"/>
399                                      </xsl:variable>
400                                      <xsl:choose>
401                                        <xsl:when test="count($sibling-reference) eq 1">
402                                          <xsl:apply-templates mode="find-target-element" select="$sibling-reference"/>
403                                        </xsl:when>
404                                        <!-- If all else fails, give up and just use the first one -->
405                                        <xsl:otherwise>
406                                          <xsl:apply-templates mode="find-target-element" select="$referenced-elements[1]"/>
407                                        </xsl:otherwise>
408                                      </xsl:choose>
409                                    </xsl:otherwise>
410                                  </xsl:choose>
411                                </xsl:otherwise>
412                              </xsl:choose>
413                            </xsl:variable>
414                            <xsl:if test="not($result)">
415                              <xsl:message>Unable to find referenced ID: <xsl:value-of select="$ref/@refid"/></xsl:message>
416                            </xsl:if>
417                            <xsl:sequence select="$result"/>
418                          </xsl:function>
419
420                                  <xsl:template mode="find-target-element" match="compound | member">
421                                    <xsl:sequence select="."/>
422                                  </xsl:template>
423
424                                  <!-- In the index XML, enumvalue "members" immediately follow the corresponding enum member -->
425                                  <xsl:template mode="find-target-element" match="member[@kind eq 'enumvalue']">
426                                    <xsl:sequence select="preceding-sibling::member[@kind eq 'enum'][1]"/>
427                                  </xsl:template>
428
429</xsl:stylesheet>
430