docb_edoc_xml_cb.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,155 行 · 第 1/3 页
ERL
1,155 行
%% ``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-------------------------------------------------------%% ERLREFmodule(Element, _Opts) -> XML = layout_module(Element), xmerl:export_simple([XML], docb_xmerl_xml_cb, []).%% CHAPTERoverview(Element, _Opts) -> XML = layout_chapter(Element), xmerl:export_simple([XML], docb_xmerl_xml_cb, []).%%--Internal functions--------------------------------------------------layout_module(#xmlElement{name = module, content = Es}=E) -> 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 = [Ef || Ef <- get_content(functions, Es)], SortedFs = lists:sort([{function_name(Ef), Ef} || Ef <- Functions]), 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(SortedFs), 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,[""]} ]}, 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 fixotp_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; Tag==center; Tag==font -> 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 -> case get_attrval(border, E) of "" -> % implies border="0" [{p, otp_xmlify_table(E#xmlElement.content)}]; "0" -> [{p, otp_xmlify_table(E#xmlElement.content)}]; _Val -> 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 end;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) ->
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?