diff options
author | Lars Thorsen <[email protected]> | 2011-11-16 15:10:18 +0100 |
---|---|---|
committer | Lars Thorsen <[email protected]> | 2011-11-18 14:28:43 +0100 |
commit | 2da3a1fb06caf0c7fc53a8efdfbc769278b4323f (patch) | |
tree | 1c134e180ab90756f970a2e9e1c7fe9c87dc0f1e /lib/docbuilder/src/docb_edoc_xml_cb.erl | |
parent | 8dc7d8f4931f3c0cb1838c828ae54da08f943f74 (diff) | |
download | otp-2da3a1fb06caf0c7fc53a8efdfbc769278b4323f.tar.gz otp-2da3a1fb06caf0c7fc53a8efdfbc769278b4323f.tar.bz2 otp-2da3a1fb06caf0c7fc53a8efdfbc769278b4323f.zip |
[erl_docgen] Move files from docbuilder to erl_docgen
Diffstat (limited to 'lib/docbuilder/src/docb_edoc_xml_cb.erl')
-rw-r--r-- | lib/docbuilder/src/docb_edoc_xml_cb.erl | 1154 |
1 files changed, 0 insertions, 1154 deletions
diff --git a/lib/docbuilder/src/docb_edoc_xml_cb.erl b/lib/docbuilder/src/docb_edoc_xml_cb.erl deleted file mode 100644 index 90491bc007..0000000000 --- a/lib/docbuilder/src/docb_edoc_xml_cb.erl +++ /dev/null @@ -1,1154 +0,0 @@ -%% ``The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved via the world wide web at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. See -%% the Licence for the specific language governing rights and limitations -%% under the License. -%% -%% The Initial Developer of the Original Code is Ericsson AB. -%% Portions created by Ericsson are Copyright 1999-2006, Ericsson AB. -%% All Rights Reserved.�� -%% -%% $Id$ -%% --module(docb_edoc_xml_cb). - -%% This is the EDoc callback module for creating DocBuilder erlref -%% documents (man pages) in XML format, and also a DocBuilder chapter -%% document based on "overview.edoc". -%% -%% Usage examples: -%% docb_gen File -%% docb_gen -chapter overview.edoc -%% or (from an Erlang shell) -%% edoc:file(File, [{layout,docb_edoc_xml_cb},{file_suffix,".xml"}, -%% {preprocess,true}]). -%% -%% The origin of this file is the edoc module otpsgml_layout.erl -%% written by Richard Carlsson. - --export([module/2, overview/2]). - --include("xmerl.hrl"). - --define(NL, "\n"). - -%%-User interface------------------------------------------------------- - -%% ERLREF -module(Element, Opts) -> - SortP = proplists:get_value(sort_functions, Opts, true), - XML = layout_module(Element, SortP), - xmerl:export_simple([XML], docb_xmerl_xml_cb, []). - -%% CHAPTER -overview(Element, _Opts) -> - XML = layout_chapter(Element), - xmerl:export_simple([XML], docb_xmerl_xml_cb, []). - -%%--Internal functions-------------------------------------------------- - -layout_module(#xmlElement{name = module, content = Es}=E, SortP) -> - Name = get_attrval(name, E), - Desc = get_content(description, Es), - ShortDesc = text_only(get_content(briefDescription, Desc)), - FullDesc = otp_xmlify(get_content(fullDescription, Desc)), - Types0 = get_content(typedecls, Es), - Types1 = lists:sort([{type_name(Et), Et} || Et <- Types0]), - Functions = - case SortP of - true -> - lists:sort([{function_name(Ef), Ef} || - Ef <- get_content(functions, Es)]); - false -> - [{function_name(Ef), Ef} || - Ef <- get_content(functions, Es)] - end, - Header = {header, [ - ?NL,{title, [Name]}, - ?NL,{prepared, [""]}, - ?NL,{responsible, [""]}, - ?NL,{docno, ["1"]}, - ?NL,{approved, [""]}, - ?NL,{checked, [""]}, - ?NL,{date, [""]}, - ?NL,{rev, ["A"]}, - ?NL,{file, [Name++".xml"]} - ]}, - Module = {module, [Name]}, - ModuleSummary = {modulesummary, ShortDesc}, - Description = {description, [?NL|FullDesc]}, - Types = case Types1 of - [] -> []; - _ -> - [?NL, {section,[{title,["DATA TYPES"]}, - {marker,[{id,"types"}],[]}, - ?NL|types(Types1)]}] - end, - Funcs = functions(Functions), - See = seealso_module(Es), - Authors = {authors, authors(Es)}, - {erlref, - [?NL,Header, - ?NL,Module, - ?NL,ModuleSummary, - ?NL,Description] - ++ Types ++ - [?NL,Funcs, - ?NL,See, - ?NL,Authors] - }. - -layout_chapter(#xmlElement{name=overview, content=Es}) -> - Title = get_text(title, Es), - Header = {header, [ - ?NL,{title,[Title]}, - ?NL,{prepared,[""]}, - ?NL,{docno,[""]}, - ?NL,{date,[""]}, - ?NL,{rev,[""]}, - ?NL,{file, ["chapter.xml"]} - ]}, - DescEs = get_content(description, Es), - FullDescEs = get_content(fullDescription, DescEs), - Sections = chapter_ify(FullDescEs, first), - {chapter, [?NL, Header, ?NL | Sections]}. - -chapter_ify([], _) -> - []; -chapter_ify(Es, first) -> - %% Everything up to the first section should be made into - %% plain paragraphs -- or if no first section is found, everything - %% should be made into one - case find_next(h3, Es) of - {Es, []} -> - SubSections = subchapter_ify(Es, first), - [{section, [?NL,{title,["Overview"]}, - ?NL | SubSections]}]; - {FirstEs, RestEs} -> - otp_xmlify(FirstEs) ++ chapter_ify(RestEs, next) - end; -chapter_ify([#xmlElement{name=h3} = E | Es], next) -> - {SectionEs, RestEs} = find_next(h3, Es), - SubSections = subchapter_ify(SectionEs, first), - {Marker, Title} = chapter_title(E), - [{section, [?NL,{marker,[{id,Marker}],[]}, - ?NL,{title,[Title]}, - ?NL | SubSections]} | chapter_ify(RestEs, next)]. - -subchapter_ify([], _) -> - []; -subchapter_ify(Es, first) -> - %% Everything up to the (possible) first subsection should be - %% made into plain paragraphs - {FirstEs, RestEs} = find_next(h4, Es), - otp_xmlify(FirstEs) ++ subchapter_ify(RestEs, next); -subchapter_ify([#xmlElement{name=h4} = E | Es], next) -> - {SectionEs, RestEs} = find_next(h4, Es), - Elements = otp_xmlify(SectionEs), - {Marker, Title} = chapter_title(E), - [{section, [?NL,{marker,[{id,Marker}],[]}, - ?NL,{title,[Title]}, - ?NL | Elements]} | subchapter_ify(RestEs, next)]. - -chapter_title(#xmlElement{content=Es}) -> % name = h3 | h4 - case Es of - [#xmlElement{name=a} = E] -> - {get_attrval(name, E), get_text(E)} - end. - -%%--XHTML->XML transformation------------------------------------------- - -%% otp_xmlify(Es1) -> Es2 -%% Es1 = Es2 = [#xmlElement{} | #xmlText{}] -%% Fix things that are allowed in XHTML but not in chapter/erlref DTDs. -%% 1) lists (<ul>, <ol>, <dl>) and code snippets (<pre>) can not occur -%% within a <p>, such a <p> must be splitted into a sequence of <p>, -%% <ul>, <ol>, <dl> and <pre>. -%% 2) <a> must only have either a href attribute (corresponds to a -%% <seealso> or <url> in the XML code) in which case its content -%% must be plain text; or a name attribute (<marker>). -%% 3a) <b> content must be plain text. -%% b) <em> content must be plain text (or actually a <code> element -%% as well, but a simplification is used here). -%% c) <pre> content must be plain text (or could actually contain -%% <input> as well, but a simplification is used here). -%% 4) <code> content must be plain text, or the element must be split -%% into a list of elements. -%% 5a) <h1>, <h2> etc is not allowed - replaced by its content within -%% a <b> tag. -%% b) <center> is not allowed - replaced by its content. -%% c) <font> -"- -%% 6) <table> is not allowed in erlref, translated to text instead. -%% Also a <table> in chapter without a border is translated to text. -%% A <table> in chapter with a border must contain a <tcaption>. -%% 7) <sup> is not allowed - is replaced with its text content -%% within parenthesis. -%% 8) <blockquote> contents may need to be made into paragraphs -%% 9) <th> (table header) is not allowed - is replaced by -%% <td><em>...</em></td>. -otp_xmlify([]) -> - []; -otp_xmlify(Es0) -> - Es = case is_paragraph(hd(Es0)) of - - %% If the first element is a <p> xmlElement, then - %% the entire element list is ready to be otp_xmlified. - true -> - Es0; - - %% If the first element is not a <p> xmlElement, then all - %% elements up to the first <p> (or end of list) must be - %% made into a paragraph (using the {p, Es} construction) - %% before the list is otp_xmlified. - false -> - case find_next(p, Es0, []) of - {[#xmlText{value=Str}] = First, Rest} -> - %% Special case: Making a paragraph out of a - %% blank line isn't a great idea. - case is_empty(Str) of - true -> - Rest; - false -> - [{p,First}|Rest] - end; - {First, Rest} -> - [{p,First}|Rest] - end - end, - - %% Fix paragraph breaks not needed in XHTML but in XML - EsFixed = otp_xmlify_fix(Es), - - otp_xmlify_es(EsFixed). - -%% EDoc does not always translate empty lines (with leading "%%") -%% as paragraph break, this is the fix -otp_xmlify_fix(Es) -> - otp_xmlify_fix(Es, []). -otp_xmlify_fix([#xmlText{value="\n \n"++_} = E1, E2 | Es], Res) -> - %% This is how it looks when generating an erlref from a .erl file - case is_paragraph(E2) of - false -> - {P, After} = find_p_ending(Es, []), - otp_xmlify_fix(After, [{p, [E2|P]}, E1 | Res]); - true -> - otp_xmlify_fix([E2|Es], [E1|Res]) - end; -otp_xmlify_fix([#xmlText{value="\n\n"} = E1, E2 | Es], Res) -> - %% This is how it looks when generating a chapter from overview.edoc - case is_paragraph(E2) of - false -> - {P, After} = find_p_ending(Es, []), - otp_xmlify_fix(After, [{p, [E2|P]}, E1 | Res]); - true -> - otp_xmlify_fix([E2|Es], [E1|Res]) - end; -otp_xmlify_fix([E|Es], Res) -> - otp_xmlify_fix(Es, [E|Res]); -otp_xmlify_fix([], Res) -> - lists:reverse(Res). - -otp_xmlify_es([E | Es]) -> - case is_paragraph(E) of - true -> - case otp_xmlify_psplit(E) of - - %% paragraph contained inline tags and text only - nosplit -> - otp_xmlify_e(E) ++ otp_xmlify_es(Es); - - %% paragraph contained dl, ul and/or pre and has been - %% splitted - SubEs -> - lists:flatmap(fun otp_xmlify_e/1, SubEs) ++ - otp_xmlify_es(Es) - end; - false -> - otp_xmlify_e(E) ++ otp_xmlify_es(Es) - end; -otp_xmlify_es([]) -> - []. - -%% otp_xmlify_psplit(P) -> nosplit | [E] -%% Handles case 1) above. -%% Uses the {p, Es} construct, thus replaces an p xmlElement with one -%% or more {p, Es} tuples if splitting the paraghrap is necessary. -otp_xmlify_psplit(P) -> - otp_xmlify_psplit(p_content(P), [], []). -otp_xmlify_psplit([#xmlElement{name=Name}=E | Es], Content, Res) -> - if - Name==blockquote; Name==ul; Name==ol; Name==dl; Name==pre; - Name==table -> - case Content of - [] -> - otp_xmlify_psplit(Es, [], [E|Res]); - [#xmlText{value=Str}] -> - %% Special case: Making a paragraph out of a blank - %% line isn't a great idea. Instead, this can be - %% viewed as the case above, where there is no - %% content to make a paragraph out of - case is_empty(Str) of - true -> - otp_xmlify_psplit(Es, [], [E|Res]); - false -> - Pnew = {p, lists:reverse(Content)}, - otp_xmlify_psplit(Es, [], [E,Pnew|Res]) - end; - _ -> - Pnew = {p, lists:reverse(Content)}, - otp_xmlify_psplit(Es, [], [E,Pnew|Res]) - end; - - true -> - otp_xmlify_psplit(Es, [E|Content], Res) - end; -otp_xmlify_psplit([E | Es], Content, Res) -> - otp_xmlify_psplit(Es, [E|Content], Res); -otp_xmlify_psplit([], _Content, []) -> - nosplit; -otp_xmlify_psplit([], [], Res) -> - lists:reverse(Res); -otp_xmlify_psplit([], [#xmlText{value="\n\n"}], Res) -> - lists:reverse(Res); -otp_xmlify_psplit([], Content, Res) -> - Pnew = {p, lists:reverse(Content)}, - lists:reverse([Pnew|Res]). - -%% otp_xmlify_e(E) -> [E] -%% This is the function which does the actual transformation of -%% single elements, normally by making sure the content corresponds -%% to what is allowed by the OTP DTDs. -%% Returns a list of elements as the xmlification in some cases -%% returns no element or more than one element (although in most cases -%% exactly one element). -otp_xmlify_e(#xmlElement{name=a} = E) -> % 2) above - otp_xmlify_a(E); -otp_xmlify_e(#xmlElement{name=Tag} = E) % 3a-c) - when Tag==b; Tag==em; Tag==pre -> - Content = text_only(E#xmlElement.content), - [E#xmlElement{content=Content}]; -otp_xmlify_e(#xmlElement{name=code} = E) -> % 4) - case catch text_only(E#xmlElement.content) of - {'EXIT', _Error} -> - otp_xmlify_code(E); - Content -> - [E#xmlElement{content=Content}] - end; -otp_xmlify_e(#xmlElement{name=Tag} = E) % 5a - when Tag==h1; Tag==h2; Tag==h3; Tag==h4; Tag==h5 -> - Content = text_only(E#xmlElement.content), - [E#xmlElement{name=b, content=Content}]; -otp_xmlify_e(#xmlElement{name=Tag} = E) % 5b-c) - when Tag==center; - Tag==font -> - otp_xmlify_e(E#xmlElement.content); -otp_xmlify_e(#xmlElement{name=table} = E) -> % 6) - case parent(E) of - module -> - otp_xmlify_table(E#xmlElement.content); - overview -> - Content0 = otp_xmlify_e(E#xmlElement.content), - Summary = #xmlText{value=get_attrval(summary, E)}, - TCaption = E#xmlElement{name=tcaption, - attributes=[], - content=[Summary]}, - Content = Content0 ++ [TCaption], - [E#xmlElement{attributes=[], content=Content}] - end; -otp_xmlify_e(#xmlElement{name=tbody} = E) -> - otp_xmlify_e(E#xmlElement.content); -otp_xmlify_e(#xmlElement{name=sup} = E) -> % 7) - Text = get_text(E), - [#xmlText{parents = E#xmlElement.parents, - pos = E#xmlElement.pos, - language = E#xmlElement.language, - value = "(" ++ Text ++ ")"}]; -otp_xmlify_e(#xmlElement{name=blockquote} = E) -> % 8) - Content = otp_xmlify_blockquote(E#xmlElement.content), - [E#xmlElement{content=Content}]; -otp_xmlify_e(#xmlElement{name=th} = E) -> % 9) - Content = otp_xmlify_e(E#xmlElement.content), - EmE = E#xmlElement{name=em, content=Content}, - [E#xmlElement{name=td, content=[EmE]}]; -otp_xmlify_e(#xmlElement{name=p} = E) -> % recurse - Content = otp_xmlify_e(E#xmlElement.content), - [E#xmlElement{content=Content}]; -otp_xmlify_e({p, Content1}) -> - Content2 = otp_xmlify_e(Content1), - [{p, Content2}]; -otp_xmlify_e(#xmlElement{name=ul} = E) -> - Content = otp_xmlify_e(E#xmlElement.content), - [E#xmlElement{content=Content}]; -otp_xmlify_e(#xmlElement{name=li} = E) -> - %% Content may need to be made into <p>s etc. - Content = otp_xmlify(E#xmlElement.content), - [E#xmlElement{content=Content}]; -otp_xmlify_e(#xmlElement{name=dl} = E) -> - Content0 = otp_xmlify_e(E#xmlElement.content), - Content = otp_xmlify_dl(Content0), - [E#xmlElement{content=Content}]; -otp_xmlify_e(#xmlElement{name=dt} = E) -> - %% Special fix: Markers in <taglist> <tag>s are not allowed, - %% save it using 'put' and place the marker first in the <item> - %% instead - Content = case E#xmlElement.content of - [#xmlElement{name=a} = A] -> - put(dt_marker, otp_xmlify_e(A)), - otp_xmlify_e(A#xmlElement.content); - _ -> - otp_xmlify_e(E#xmlElement.content) - end, - [E#xmlElement{content=Content}]; -otp_xmlify_e(#xmlElement{name=dd} = E) -> - %% Content may need to be made into <p>s etc. - Content0 = otp_xmlify(E#xmlElement.content), - Content = case get(dt_marker) of - undefined -> Content0; - [Marker] -> - put(dt_marker, undefined), - [Marker#xmlElement{content=[]}|Content0] - end, - [E#xmlElement{content=Content}]; -otp_xmlify_e(#xmlElement{name=tr} = E) -> - Content = otp_xmlify_e(E#xmlElement.content), - [E#xmlElement{content=Content}]; -otp_xmlify_e(#xmlElement{name=td} = E) -> - Content = otp_xmlify_e(E#xmlElement.content), - [E#xmlElement{content=Content}]; -otp_xmlify_e([E | Es]) -> - otp_xmlify_e(E) ++ otp_xmlify_e(Es); -otp_xmlify_e([]) -> - []; -otp_xmlify_e(E) -> - [E]. - -%%--Tags with special handling------------------------------------------ - -%% otp_xmlify_a(A1) -> [A2] -%% Takes an <a> element and filters the attributes to decide wheather -%% its a seealso/url or a marker. -%% In the case of a seealso/url, the href part is checked, making -%% sure a .xml/.html file extension is removed (as DocBuilder inserts -%% .html extension when resolving cross references). -%% Also, references to other applications //App has a href attribute -%% value "OTPROOT/..." (due to app_default being set to "OTPROOT" in -%% docb_gen.erl), in this case both href attribute and content must be -%% formatted correctly according to DocBuilder requirements. -otp_xmlify_a(A) -> - [Attr0] = filter_a_attrs(A#xmlElement.attributes), - case Attr0 of - #xmlAttribute{name=href, value=Href0} -> % seealso | url - Content0 = text_only(A#xmlElement.content), - {Href, Content} = otp_xmlify_a_href(Href0, Content0), - [A#xmlElement{attributes=[Attr0#xmlAttribute{value=Href}], - content=Content}]; - #xmlAttribute{name=name} -> % marker - Content = otp_xmlify_e(A#xmlElement.content), - [A#xmlElement{attributes=[Attr0], content=Content}] - end. - -%% filter_a_attrs(Attrs) -> [Attr] -%% Removes all attributes from a <a> element except the href or -%% name attribute. -filter_a_attrs([#xmlAttribute{name=href} = Attr | _Attrs]) -> - [Attr]; -filter_a_attrs([#xmlAttribute{name=name} = Attr | _Attrs]) -> - [Attr]; -filter_a_attrs([_Attr|Attrs]) -> - filter_a_attrs(Attrs); -filter_a_attrs([]) -> - []. - -%% otp_xmlify_a_href(Href0, Es0) -> {Href1, Es1} -%% Href = string() -otp_xmlify_a_href("#"++_ = Marker, Es0) -> % <seealso marker="#what"> - {Marker, Es0}; -otp_xmlify_a_href("http:"++_ = URL, Es0) -> % external URL - {URL, Es0}; -otp_xmlify_a_href("OTPROOT"++AppRef, Es0) -> % <.. marker="App:FileRef - [AppS, "doc", FileRef1] = split(AppRef, "/"), - FileRef = AppS++":"++otp_xmlify_a_fileref(FileRef1, AppS), - [#xmlText{value=Str0} = T] = Es0, - Str = case split(Str0, "/") of - %% //Application - [AppS2] -> - %% AppS2 can differ from AppS - %% Example: xmerl/XMerL - AppS2; - [_AppS,ModRef] -> - case split(ModRef, ":") of - %% //Application/Module - [Module] -> - Module++"(3)"; - %% //Application/Module:Type() - [_Module,_Type] -> - ModRef - end; - %% //Application/Module:Function/Arity - [_AppS,ModFunc,Arity] -> - ModFunc++"/"++Arity - end, - {FileRef, [T#xmlText{value=Str}]}; -otp_xmlify_a_href("../"++File, Es0) -> - %% Special case: This kind of relative path is used on some - %% places within i.e. EDoc and refers to a file within the same - %% application tree. - %% Correct the path according to the OTP directory structure - {"../../"++File, Es0}; -otp_xmlify_a_href(FileRef1, Es0) -> % File within the same application - FileRef2 = otp_xmlify_a_fileref(FileRef1, this), - {FileRef2, Es0}. - -%% otp_xmlify_a_fileref(FileRef1, AppS|this) -> FileRef2 -%% AppS = FileRef = string() -otp_xmlify_a_fileref(FileRef1, AppS) -> - case split(FileRef1, ".#") of - - %% EDoc default name is "overview-summary.html, - %% name of OTP User's Guide chapter is "chapter.xml" - ["overview-summary", _Ext] -> - "chapter"; - ["overview-summary", _Ext, Marker] -> - "chapter#"++Marker; - - [File, Ext] when Ext=="xml"; - Ext=="html", AppS/=this -> - File; - [File, Ext, Marker0] -> - %% Here is an awkward solution to an awkward problem - %% The marker automatically inserted by DocBuilder at - %% each function does not seem to work for EDoc generated - %% ERLREFs. - %% So if the referenced marker is in an ERLREF generated - %% by EDoc, keep it "as is", ie "function-arity". - %% If the referenced marker is NOT in an ERLREF generated - %% by EDoc, the marker should be on the format - %% "function/arity". - %% The awkward part of the solution is to decide wheather - %% the ERLREF is generated by EDoc or not: Here we make - %% the decision based on which application the module - %% belongs to -- which is ok when the module was written - %% but probably not in the future... - EDocApps = ["edoc","hipe","syntax_tools","xmerl"], - IsEDocGenerated = lists:member(AppS, EDocApps), - Marker = if - %% The marker is in a file in *this* - %% application (which documentation obviously - %% is generated by EDoc), or it is in a file - %% in an application which documentation - %% is assumed to be generated by EDoc - AppS==this; IsEDocGenerated -> - Marker0; - - %% The marker is in a file in an application - %% which documentation is assumed NOT to be - %% generated by EDoc - true -> - case split(Marker0, "-") of - [Func,Arity] -> - Func++"/"++Arity; - _ -> - Marker0 - end - end, - if - %% Ignore file extension in file reference if it either - %% is ".xml" or if it is ".html" but AppS/=this, that - %% is, we're resolving an OTPROOT file reference - Ext=="xml"; - Ext=="html", AppS/=this -> - File++"#"++Marker; - true -> - File++"."++Ext++"#"++Marker - end; - - %% References to other files than XML files are kept as-is - _ -> - FileRef1 - end. - -%% otp_xmlify_blockquote(Es1) -> Es2 -%% Ensures that the content of a <blockquote> is divided into -%% <p>s using the {p, Es} construct. -otp_xmlify_blockquote([#xmlElement{name=p} = E|Es]) -> - [E | otp_xmlify_blockquote(Es)]; -otp_xmlify_blockquote([#xmlText{} = E|Es]) -> - {P, After} = find_p_ending(Es, []), - [{p, [E|P]} | otp_xmlify_blockquote(After)]; -otp_xmlify_blockquote([]) -> - []. - -%% otp_xmlify_code(E) -> Es -%% Takes a <code> xmlElement and split it into a list of <code> and -%% other xmlElements. Necessary when it contains more than a single -%% xmlText element. -%% Example: -%% #xmlElement{name=code, -%% content=[#xmlText{}, #xmlElement{name=br}, #xmlText{}]} -%% => -%% [#xmlElement{name=code, content=[#xmlText{}]}, -%% #xmlElement{name=br}, -%% #xmlElement{name=code, content=[#xmlText{}]}] -otp_xmlify_code(E) -> - otp_xmlify_code(E, E#xmlElement.content, []). -otp_xmlify_code(Code, [#xmlText{} = E|Es], Acc) -> - otp_xmlify_code(Code, Es, [Code#xmlElement{content=[E]}|Acc]); -otp_xmlify_code(Code, [#xmlElement{} = E|Es], Acc) -> - otp_xmlify_code(Code, Es, [E|Acc]); -otp_xmlify_code(_Code, [], Acc) -> - lists:reverse(Acc). - -%% otp_xmlify_dl(Es1) -> Es2 -%% Insert empty <dd> elements if necessary. -%% OTP DTDs does not allow <taglist>s with <tag>s but no <item>s. -otp_xmlify_dl([#xmlElement{name=dt} = E|Es]) -> - [E|otp_xmlify_dl(Es, E)]; -otp_xmlify_dl([E|Es]) -> - [E|otp_xmlify_dl(Es)]; -otp_xmlify_dl([]) -> - []. - -otp_xmlify_dl([#xmlElement{name=dd} = E|Es], _DT) -> - [E|otp_xmlify_dl(Es)]; -otp_xmlify_dl([#xmlElement{name=dt} = E|Es], DT) -> - DD = DT#xmlElement{name=dd, attributes=[], content=[]}, - [DD,E|otp_xmlify_dl(Es, E)]; -otp_xmlify_dl([E|Es], DT) -> - [E|otp_xmlify_dl(Es, DT)]; -otp_xmlify_dl([], DT) -> - DD = DT#xmlElement{name=dd, attributes=[], content=[]}, - [DD]. - -%% otp_xmlify_table(Es1) -> Es2 -%% Transform <table> contents into "text", that is, inline elements. -otp_xmlify_table([#xmlText{} = E|Es]) -> - [E | otp_xmlify_table(Es)]; -otp_xmlify_table([#xmlElement{name=tbody} = E|Es]) -> - otp_xmlify_table(E#xmlElement.content)++otp_xmlify_table(Es); -otp_xmlify_table([#xmlElement{name=tr, content=Content}|Es]) -> - %% Insert newlines between table rows - otp_xmlify_table(Content)++[{br,[]}]++otp_xmlify_table(Es); -otp_xmlify_table([#xmlElement{name=th, content=Content}|Es]) -> - [{em, Content} | otp_xmlify_table(Es)]; -otp_xmlify_table([#xmlElement{name=td, content=Content}|Es]) -> - otp_xmlify_e(Content) ++ otp_xmlify_table(Es); -otp_xmlify_table([]) -> - []. - -%%--Misc help functions used by otp_xmlify/1 et al--------------------- - -%% find_next(Tag, Es) -> {Es1, Es2} -%% Returns {Es1, Es2} where Es1 is the list of all elements up to (but -%% not including) the first element with tag Tag in Es, and Es2 -%% is the remaining elements of Es. -find_next(Tag, Es) -> - find_next(Tag, Es, []). -find_next(Tag, [#xmlElement{name=Tag} = E | Es], AccEs) -> - {lists:reverse(AccEs), [E|Es]}; -find_next(Tag, [E|Es], AccEs) -> - find_next(Tag, Es, [E|AccEs]); -find_next(_Tag, [], AccEs) -> - {lists:reverse(AccEs), []}. - -%% find_p_ending(Es, []) -> {Es1, Es2} -%% Returns {Es1, Es2} where Es1 is the list of all elements up to (but -%% not including) the first paragraph break in Es, and Es2 is -%% the remaining elements of Es2. -%% Paragraph break = <p> tag or empty line -%% the next blank line, <p> or end-of-list as P, and the remaining -%% elements of Es as After. -find_p_ending([#xmlText{value="\n \n"++_} = E|Es], P) -> - {lists:reverse(P), [E|Es]}; -find_p_ending([#xmlElement{name=p} = E|Es], P) -> - {lists:reverse(P), [E|Es]}; -find_p_ending([E|Es], P) -> - find_p_ending(Es, [E|P]); -find_p_ending([], P) -> - {lists:reverse(P), []}. - -%% is_paragraph(E | P) -> bool() -%% P = {p, Es} -is_paragraph(#xmlElement{name=p}) -> true; -is_paragraph({p, _Es}) -> true; -is_paragraph(_E) -> false. - -%% p_content(E | P) -> Es -p_content(#xmlElement{content=Content}) -> Content; -p_content({p, Content}) -> Content. - -%% is_empty(Str) -> bool() -%% Str = string() -%% Returns true if Str is empty in the sense that it contains nothing -%% but spaces, tabs or newlines. -is_empty("\n"++Str) -> - is_empty(Str); -is_empty(" "++Str) -> - is_empty(Str); -is_empty("\t"++Str) -> - is_empty(Str); -is_empty("") -> - true; -is_empty(_) -> - false. - -%% split(Str, Seps) -> [Str] -split(Str, Seps) -> - split(Str, Seps, []). - -split([Ch|Str], Seps, Acc) -> - case lists:member(Ch, Seps) of - true -> split(Str, Seps, Acc); - false -> split(Str, Seps, Acc, [Ch]) - end; -split([], _Seps, Acc) -> - lists:reverse(Acc). - -split([Ch|Str], Seps, Acc, Chs) -> - case lists:member(Ch, Seps) of - true -> split(Str, Seps, [lists:reverse(Chs)|Acc]); - false -> split(Str, Seps, Acc, [Ch|Chs]) - end; -split([], _Seps, Acc, Chs) -> - lists:reverse([lists:reverse(Chs)|Acc]). - -%%--Functions for creating an erlref document--------------------------- - -%% function_name(F) -> string() -%% F = #xmlElement{name=function} -%% Returns the name of a function as "name/arity". -function_name(E) -> - get_attrval(name, E) ++ "/" ++ get_attrval(arity, E). - -%% functions(Fs) -> Es -%% Fs = [{Name, F}] -%% Name = string() "name/arity" -%% F = #xmlElement{name=function} -functions(Fs) -> - Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs), - if - Es==[] -> - []; - true -> - {funcs, Es} - end. - -function(_Name, E=#xmlElement{content = Es}) -> - TypeSpec = get_content(typespec, Es), - [?NL,{func, [ ?NL, - {name, - case funcheader(TypeSpec) of - [] -> - signature(get_content(args, Es), - get_attrval(name, E)); - Spec -> Spec - end - }, - ?NL,{fsummary, fsummary(Es)}, - ?NL,local_types(TypeSpec), - ?NL,{desc, - label_anchor(E)++ - deprecated(Es)++ - fulldesc(Es)++ - seealso_function(Es)} - ]}]. - -fsummary([]) -> ["\s"]; -fsummary(Es) -> - Desc = get_content(description, Es), - case get_content(briefDescription, Desc) of - [] -> - fsummary_equiv(Es); % no description at all if no equiv - ShortDesc -> - text_only(ShortDesc) - end. - -fsummary_equiv(Es) -> - case get_content(equiv, Es) of - [] -> ["\s"]; - Es1 -> - case get_content(expr, Es1) of - [] -> ["\s"]; - [Expr] -> - ["Equivalent to ", Expr, ".",?NL] - end - end. - -label_anchor(E) -> - case get_attrval(label, E) of - "" -> []; - Ref -> [{marker, [{id, Ref}],[]},?NL] - end. - -label_anchor(Content, E) -> - case get_attrval(label, E) of - "" -> Content; - Ref -> {p,[{marker, [{id, Ref}],[]}, - {em, Content}]} - end. - -signature(Es, Name) -> - [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()", ?NL]. - -arg(#xmlElement{content = Es}) -> - [get_text(argName, Es)]. - -funcheader([]) -> []; -funcheader(Es) -> - [t_name(get_elem(erlangName, Es))] ++ t_utype(get_elem(type, Es)). - -local_types([]) -> []; -local_types(Es) -> - local_defs2(get_elem(localdef, Es)). - -local_defs2([]) -> []; -local_defs2(Es) -> - {type,[?NL | [{v, localdef2(E)} || E <- Es]]}. - -%% Like localdef/1, but does not use label_anchor/2 -- we don't want any -%% markers or em tags in <v> tag, plain text only! -localdef2(#xmlElement{content = Es}) -> - case get_elem(typevar, Es) of - [] -> - t_utype(get_elem(type, Es)); - [V] -> - t_var(V) ++ [" = "] ++ t_utype(get_elem(type, Es)) - end. - -type_name(#xmlElement{content = Es}) -> - t_name(get_elem(erlangName, get_content(typedef, Es))). - -types(Ts) -> - Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E) end, Ts), - [?NL, {taglist,[?NL|Es]}]. - -typedecl(Name, #xmlElement{content = Es}) -> - TypedefEs = get_content(typedef, Es), - Id = "type-"++Name, - [{tag, typedef(TypedefEs)}, - ?NL, - {item, [{marker,[{id,Id}],[]} | - local_defs(get_elem(localdef, TypedefEs)) ++ fulldesc(Es)]}, - ?NL]. - -typedef(Es) -> - Name = ([t_name(get_elem(erlangName, Es)), "("] - ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), [")"])), - case get_elem(type, Es) of - [] -> - [{tt, Name}]; - Type -> - [{tt, Name ++ [" = "] ++ t_utype(Type)}] - end. - -local_defs([]) -> []; -local_defs(Es) -> - [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}]. - -localdef(E = #xmlElement{content = Es}) -> - Var = case get_elem(typevar, Es) of - [] -> - [label_anchor(t_abstype(get_content(abstype, Es)), E)]; - [V] -> - t_var(V) - end, - Var ++ [" = "] ++ t_utype(get_elem(type, Es)). - -deprecated(Es) -> - case get_content(deprecated, Es) of - [] -> []; - DeprEs -> - Es2 = get_content(fullDescription, - get_content(description, DeprEs)), - Es3 = otp_xmlify_e(Es2), - [{p, [{em, ["This function is deprecated: "]} |Es3]}, ?NL] - end. - -fulldesc(Es) -> - case get_content(fullDescription, get_content(description, Es)) of - [] -> - index_desc(Es); - Desc -> - [?NL|otp_xmlify(Desc)] ++ [?NL] - end. - -index_desc(Es) -> - Desc = get_content(description, Es), - case get_content(briefDescription, Desc) of - [] -> - equiv(Es); % no description at all if no equiv - ShortDesc -> - ShortDesc - end. - -seealso_module(Es) -> - case get_elem(see, Es) of - [] -> []; - Es1 -> - {section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]} - end. -seealso_function(Es) -> - case get_elem(see, Es) of - [] -> []; - Es1 -> - [{p, [{em, ["See also:"]}, " "] ++ seq(fun see/1, Es1, ["."])}, - ?NL] - end. - -%% ELEMENT see PCDATA -%% ATTLIST name PCDATA -%% href PCDATA -see(#xmlElement{content=Es0} = E) -> - Href0 = get_attrval(href, E), - {Href, Es} = otp_xmlify_a_href(Href0, Es0), - [{seealso, [{marker, Href}], Es}]. - -equiv(Es) -> - case get_content(equiv, Es) of - [] -> ["\s"]; - Es1 -> - case get_content(expr, Es1) of - [] -> []; - [Expr] -> - Expr1 = [Expr], - Expr2 = case get_elem(see, Es1) of - [] -> - {c,Expr1}; - [E=#xmlElement{}] -> - case get_attrval(href, E) of - "" -> - {c,Expr1}; - Ref -> - {seealso, [{marker, Ref}], Expr1} - end - end, - [{p, ["Equivalent to ", Expr2, "."]}, ?NL] - end - end. - -authors(Es) -> - case get_elem(author, Es) of - [] -> - [?NL,{aname,["\s"]},?NL,{email,["\s"]}]; - Es1 -> - [?NL|seq(fun author/1, Es1, "", [])] - end. - -author(E=#xmlElement{}) -> - Name = case get_attrval(name, E) of - [] -> "\s"; - N -> N - end, - Mail = case get_attrval(email, E) of - [] -> "\s"; - M -> M - end, - [?NL,{aname,[Name]},?NL,{email,[Mail]}]. - -t_name([E]) -> - N = get_attrval(name, E), - case get_attrval(module, E) of - "" -> N; - M -> - S = M ++ ":" ++ N, - case get_attrval(app, E) of - "" -> S; - A -> "//" ++ A ++ "/" ++ S - end - end. - -t_utype([E]) -> - t_utype_elem(E). - -t_utype_elem(E=#xmlElement{content = Es}) -> - case get_attrval(name, E) of - "" -> t_type(Es); - Name -> - T = t_type(Es), - case T of - [Name] -> T; % avoid generating "Foo::Foo" - T -> [Name] ++ ["::"] ++ T - end - end. - -t_type([E=#xmlElement{name = typevar}]) -> - t_var(E); -t_type([E=#xmlElement{name = atom}]) -> - t_atom(E); -t_type([E=#xmlElement{name = integer}]) -> - t_integer(E); -t_type([E=#xmlElement{name = float}]) -> - t_float(E); -t_type([#xmlElement{name = nil}]) -> - t_nil(); -t_type([#xmlElement{name = list, content = Es}]) -> - t_list(Es); -t_type([#xmlElement{name = tuple, content = Es}]) -> - t_tuple(Es); -t_type([#xmlElement{name = 'fun', content = Es}]) -> - t_fun(Es); -t_type([#xmlElement{name = abstype, content = Es}]) -> - t_abstype(Es); -t_type([#xmlElement{name = union, content = Es}]) -> - t_union(Es); -t_type([#xmlElement{name = record, content = Es}]) -> - t_record(Es). - -t_var(E) -> - [get_attrval(name, E)]. - -t_atom(E) -> - [get_attrval(value, E)]. - -t_integer(E) -> - [get_attrval(value, E)]. - -t_float(E) -> - [get_attrval(value, E)]. - -t_nil() -> - ["[]"]. - -t_list(Es) -> - ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"]. - -t_tuple(Es) -> - ["{"] ++ seq(fun t_utype_elem/1, Es, ["}"]). - -t_fun(Es) -> - ["("] ++ seq(fun t_utype_elem/1, get_content(argtypes, Es), - [") -> "] ++ t_utype(get_elem(type, Es))). - -t_record([E|Es]) -> - ["#", get_attrval(value, E), "{"++ seq(fun t_field/1, Es) ++"}"]. -t_field(#xmlElement{name=field, content=[Atom,Type]}) -> - [get_attrval(value, Atom), "="] ++ t_utype_elem(Type). - -t_abstype(Es) -> - case split_at_colon(t_name(get_elem(erlangName, Es)),[]) of - {Mod,Type} -> - [Type, "("] ++ - seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) ++ - [" (see module ", Mod, ")"]; - Type -> - [Type, "("] ++ - seq(fun t_utype_elem/1, get_elem(type, Es), [")"]) - end. - -%% Split at one colon, but not at two (or more) -split_at_colon([$:,$:|_]=Rest,Acc) -> - lists:reverse(Acc)++Rest; -split_at_colon([$:|Type],Acc) -> - {lists:reverse(Acc),Type}; -split_at_colon([Char|Rest],Acc) -> - split_at_colon(Rest,[Char|Acc]); -split_at_colon([],Acc) -> - lists:reverse(Acc). - -t_union(Es) -> - seq(fun t_utype_elem/1, Es, " | ", []). - -%% seq(Fun, Es) -%% seq(Fun, Es, Tail) -%% seq(Fun, Es, Sep, Tail) -> [string()] -%% Fun = function(E) -> [string()] -%% Sep = string() -%% Tail = [string()] -%% Applies Fun to each element E in Es and return the appended list of -%% strings, separated by Sep which defaults to ", " and ended by Tail -%% which defaults to []. -seq(Fun, Es) -> - seq(Fun, Es, []). -seq(Fun, Es, Tail) -> - seq(Fun, Es, ", ", Tail). -seq(Fun, [E], _Sep, Tail) -> - Fun(E) ++ Tail; -seq(Fun, [E | Es], Sep, Tail) -> - Fun(E) ++ [Sep] ++ seq(Fun, Es, Sep, Tail); -seq(_Fun, [], _Sep, Tail) -> - Tail. - -%%--Misc functions for accessing fields etc----------------------------- - -%% Type definitions used below: -%% E = #xmlElement{} | #xmlText{} -%% Es = [E] -%% Tag = atom(), XHTML tag -%% Name = atom(), XHTML attribute name -%% Attrs = [#xmlAttribute{}] -%% Ts = [#xmlText{}] - -%% parent(E) -> module | overview -parent(E) -> - Parents = E#xmlElement.parents, - {Parent,_} = lists:last(Parents), - Parent. - -%% get_elem(Tag, Es1) -> Es2 -%% Returns a list of all elements in Es which have the name Tag. -get_elem(Name, [#xmlElement{name = Name} = E | Es]) -> - [E | get_elem(Name, Es)]; -get_elem(Name, [_ | Es]) -> - get_elem(Name, Es); -get_elem(_, []) -> - []. - -%% get_attr(Name, Attrs1) -> Attrs2 -%% Returns a list of all attributes in Attrs1 which have the name Name. -get_attr(Name, [#xmlAttribute{name = Name} = A | As]) -> - [A | get_attr(Name, As)]; -get_attr(Name, [_ | As]) -> - get_attr(Name, As); -get_attr(_, []) -> - []. - -%% get_attrval(Name, E) -> string() -%% If E has one attribute with name Name, return its value, otherwise "" -get_attrval(Name, #xmlElement{attributes = As}) -> - case get_attr(Name, As) of - [#xmlAttribute{value = V}] -> - V; - [] -> "" - end. - -%% get_content(Tag, Es1) -> Es2 -%% If there is one element in Es1 with name Tag, returns its contents, -%% otherwise [] -get_content(Name, Es) -> - case get_elem(Name, Es) of - [#xmlElement{content = Es1}] -> - Es1; - [] -> [] - end. - -%% get_text(Tag, Es) -> string() -%% If there is one element in Es with name Tag, and its content is -%% a single xmlText, return the value of this xmlText. -%% Otherwise return "". -get_text(Name, Es) -> - case get_content(Name, Es) of - [#xmlText{value = Text}] -> - Text; - [] -> "" - end. - -%% get_text(E) -> string() -%% Return the value of an single xmlText which is the content of E, -%% possibly recursively. -get_text(#xmlElement{content=[#xmlText{value=Text}]}) -> - Text; -get_text(#xmlElement{content=[E]}) -> - get_text(E). - -%% text_only(Es) -> Ts -%% Takes a list of xmlElement and xmlText and return a lists of xmlText. -text_only([#xmlElement{content = Content}|Es]) -> - text_only(Content) ++ text_only(Es); -text_only([#xmlText{} = E |Es]) -> - [E | text_only(Es)]; -text_only([]) -> - []. |