aboutsummaryrefslogtreecommitdiffstats
path: root/lib/edoc/src/otpsgml_layout.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/edoc/src/otpsgml_layout.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/edoc/src/otpsgml_layout.erl')
-rw-r--r--lib/edoc/src/otpsgml_layout.erl853
1 files changed, 853 insertions, 0 deletions
diff --git a/lib/edoc/src/otpsgml_layout.erl b/lib/edoc/src/otpsgml_layout.erl
new file mode 100644
index 0000000000..45f74b299e
--- /dev/null
+++ b/lib/edoc/src/otpsgml_layout.erl
@@ -0,0 +1,853 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @author Richard Carlsson <[email protected]>
+%% @author Kenneth Lundin <[email protected]>
+%% @copyright 2001-2004 Richard Carlsson
+%% @see edoc_layout
+%% @end
+%% =====================================================================
+
+%% @doc The OTP SGML layout module for EDoc. See the module {@link edoc}
+%% for details on usage.
+
+%% Note that this is written so that it is *not* depending on edoc.hrl!
+
+-module(otpsgml_layout).
+
+-export([module/2, package/2, overview/2,type/1]).
+
+-import(edoc_report, [report/2]).
+
+-include("xmerl.hrl").
+
+-define(SGML_EXPORT, xmerl_otpsgml).
+-define(DEFAULT_XML_EXPORT, ?SGML_EXPORT).
+-define(STYLESHEET, "stylesheet.css").
+-define(NL, "\n").
+-define(DESCRIPTION_TITLE, "Description").
+-define(DESCRIPTION_LABEL, "description").
+-define(DATA_TYPES_TITLE, "Data Types").
+-define(DATA_TYPES_LABEL, "types").
+-define(FUNCTION_INDEX_TITLE, "Function Index").
+-define(FUNCTION_INDEX_LABEL, "index").
+-define(FUNCTIONS_TITLE, "Function Details").
+-define(FUNCTIONS_LABEL, "functions").
+
+
+%% @doc The layout function.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {index_columns, integer()@}}
+%% </dt>
+%% <dd>Specifies the number of column pairs used for the function
+%% index tables. The default value is 1.
+%% </dd>
+%% <dt>{@type {stylesheet, string()@}}
+%% </dt>
+%% <dd>Specifies the URI used for referencing the stylesheet. The
+%% default value is `"stylesheet.css"'. If an empty string is
+%% specified, no stylesheet reference will be generated.
+%% </dd>
+%% <dt>{@type {xml_export, Module::atom()@}}
+%% </dt>
+%% <dd>Specifies an {@link //xmerl. `xmerl'} callback module to be
+%% used for exporting the documentation. See {@link
+%% //xmerl/xmerl:export_simple/3} for details.
+%% </dd>
+%% </dl>
+%%
+%% @see edoc:layout/2
+
+-record(opts, {root, stylesheet, index_columns}).
+
+module(Element, Options) ->
+ XML = layout_module(Element, init_opts(Element, Options)),
+ Export = proplists:get_value(xml_export, Options,
+ ?DEFAULT_XML_EXPORT),
+ xmerl:export_simple([XML], Export, []).
+
+% Put layout options in a data structure for easier access.
+
+init_opts(Element, Options) ->
+ R = #opts{root = get_attrval(root, Element),
+ index_columns = proplists:get_value(index_columns,
+ Options, 1)
+ },
+ case proplists:get_value(stylesheet, Options) of
+ undefined ->
+ S = edoc_lib:join_uri(R#opts.root, ?STYLESHEET),
+ R#opts{stylesheet = S};
+ "" ->
+ R; % don't use any stylesheet
+ S when is_list(S) ->
+ R#opts{stylesheet = S};
+ _ ->
+ report("bad value for option `stylesheet'.", []),
+ exit(error)
+ end.
+
+
+%% =====================================================================
+%% XML-BASED LAYOUT ENGINE
+%% =====================================================================
+
+%% We assume that we have expanded XML data.
+
+%% <!ELEMENT module (moduleName, moduleFullName, behaviour*, description?,
+%% author*, version?, since?, copyright?, deprecated?,
+%% see*, reference*, typedecls?, functions)>
+%% <!ATTLIST module
+%% root CDATA #IMPLIED>
+%% <!ELEMENT moduleName (#PCDATA)>
+%% <!ELEMENT moduleFullName (#PCDATA)>
+%% <!ELEMENT behaviour (#PCDATA)>
+%% <!ATTLIST behaviour
+%% href CDATA #IMPLIED>
+%% <!ELEMENT description (briefDescription, fullDescription?)>
+%% <!ELEMENT briefDescription (#PCDATA)>
+%% <!ELEMENT fullDescription (#PCDATA)>
+%% <!ELEMENT author EMPTY>
+%% <!ATTLIST author
+%% name CDATA #REQUIRED
+%% email CDATA #IMPLIED
+%% website CDATA #IMPLIED>
+%% <!ELEMENT version (#PCDATA)>
+%% <!ELEMENT since (#PCDATA)>
+%% <!ELEMENT copyright (#PCDATA)>
+%% <!ELEMENT deprecated (description)>
+%% <!ELEMENT see (#PCDATA)>
+%% <!ATTLIST see
+%% name CDATA #REQUIRED
+%% href CDATA #IMPLIED>
+%% <!ELEMENT reference (#PCDATA)>
+%% <!ELEMENT typedecls (typedecl+)>
+%% <!ELEMENT functions (function+)>
+
+layout_module(#xmlElement{name = module, content = Es}=E, _Opts) ->
+ Name = get_attrval(name, E),
+ Desc = get_content(description, Es),
+ ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Functions = [E || E <- get_content(functions, Es)],
+ SortedFs = lists:sort([{function_name(E), E} || E <- Functions]),
+ Types = get_content(typedecls, Es),
+ SortedTs = lists:sort([{type_name(E), E} || E <- Types]),
+ Header = {header, [
+ ?NL,{title, [Name]},
+ ?NL,{prepared, [""]},
+ ?NL,{responsible, [""]},
+ ?NL,{docno, ["1"]},
+ ?NL,{approved, [""]},
+ ?NL,{checked, [""]},
+ ?NL,{date, [""]},
+ ?NL,{rev, ["A"]},
+ ?NL,{file, [Name++".sgml"]}
+ ]},
+ Module = {module, [Name]},
+ ModuleSummary = {modulesummary, ShortDesc},
+ {Short,Long} = find_first_p(FullDesc,[]),
+ Description = {description, [?NL,{p,Short}|Long]++[?NL|types(SortedTs)]},
+ Funcs = functions(SortedFs),
+ Authors = {authors, authors(Es)},
+ See = sees1(Es),
+ {erlref, [
+ ?NL,Header,
+ ?NL,Module,
+ ?NL,ModuleSummary,
+ ?NL,Description,
+ ?NL,Funcs,
+ ?NL,See,
+ ?NL,Authors
+ ]
+ }.
+
+stylesheet(Opts) ->
+ case Opts#opts.stylesheet of
+ undefined ->
+ [];
+ CSS ->
+ [{link, [{rel, "stylesheet"},
+ {type, "text/css"},
+ {href, CSS}], []},
+ ?NL]
+ end.
+
+% doc_index(FullDesc, Functions, Types) ->
+% case doc_index_rows(FullDesc, Functions, Types) of
+% [] -> [];
+% Rs ->
+% [{ul, [{li, [{a, [{href, local_label(R)}], [T]}]}
+% || {T, R} <- Rs]}]
+% end.
+
+% doc_index_rows(FullDesc, Functions, Types) ->
+% (if FullDesc == [] -> [];
+% true -> [{?DESCRIPTION_TITLE, ?DESCRIPTION_LABEL}]
+% end
+% ++ if Types == [] -> [];
+% true -> [{?DATA_TYPES_TITLE, ?DATA_TYPES_LABEL}]
+% end
+% ++ if Functions == [] -> [];
+% true -> [{?FUNCTION_INDEX_TITLE, ?FUNCTION_INDEX_LABEL},
+% {?FUNCTIONS_TITLE, ?FUNCTIONS_LABEL}]
+% end).
+
+% function_index(Fs, Cols) ->
+% case function_index_rows(Fs, Cols, []) of
+% [] -> [];
+% Rows ->
+% [?NL,
+% {h2, [{a, [{name, ?FUNCTION_INDEX_LABEL}],
+% [?FUNCTION_INDEX_TITLE]}]},
+% ?NL,
+% {table, [{width, "100%"}, {border, 1}], Rows},
+% ?NL]
+% end.
+
+% function_index_rows(Fs, Cols, Title) ->
+% Rows = (length(Fs) + (Cols - 1)) div Cols,
+% (if Title == [] -> [];
+% true -> [{tr, [{th, [{colspan, Cols * 2}, {align, left}],
+% [Title]}]},
+% ?NL]
+% end
+% ++ lists:flatmap(fun index_row/1,
+% edoc_lib:transpose(edoc_lib:segment(Fs, Rows)))).
+
+% index_row(Fs) ->
+% [{tr, lists:flatmap(fun index_col/1, Fs)}, ?NL].
+
+% index_col({Name, F=#xmlElement{content = Es}}) ->
+% [{td, [{valign, "top"}], label_href([Name], F)},
+% {td, index_desc(Es)}].
+
+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.
+
+% label_href(Content, F) ->
+% case get_attrval(label, F) of
+% "" -> Content;
+% Ref -> [{a, [{href, local_label(Ref)}], Content}]
+% end.
+
+
+%% <!ELEMENT function (args, typespec?, equiv?, description?, since?,
+%% deprecated?, see*)>
+%% <!ATTLIST function
+%% name CDATA #REQUIRED
+%% arity CDATA #REQUIRED
+%% exported NMTOKEN(yes | no) #REQUIRED
+%% label CDATA #IMPLIED>
+%% <!ELEMENT args (arg*)>
+%% <!ELEMENT arg description?>
+%% <!ATTLIST arg name CDATA #REQUIRED>
+
+
+%% <!ELEMENT equiv (expr, see?)>
+%% <!ELEMENT expr (#PCDATA)>
+
+% functions(Fs) ->
+% Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs),
+% if Es == [] -> [];
+% true ->
+% [?NL,
+% {h2, [{a, [{name, ?FUNCTIONS_LABEL}], [?FUNCTIONS_TITLE]}]},
+% ?NL | Es]
+% end.
+
+functions(Fs) ->
+ Es = lists:flatmap(fun ({Name, E}) -> function(Name, E) end, Fs),
+ if Es == [] -> [];
+ true ->
+ {funcs, Es}
+ end.
+
+% is_exported(E) ->
+% case get_attrval(exported, E) of
+% "yes" -> true;
+% _ -> false
+% end.
+
+% function(Name, E=#xmlElement{content = Es}) ->
+% ([?NL, {h3, label_anchor([Name], E)}, ?NL]
+% ++ case typespec(get_content(typespec, Es)) of
+% [] ->
+% signature(get_content(arguments, Es),
+% get_text(functionName, Es));
+% Spec -> Spec
+% end
+% ++ equiv(Es)
+% ++ deprecated(Es, "function")
+% ++ fulldesc(Es)
+% ++ since(Es)
+% ++ sees(Es)).
+
+function(_Name, E=#xmlElement{content = Es}) ->
+ TypeSpec = get_content(typespec, Es),
+ [?NL,{func, [ ?NL,
+ {name,
+% case typespec(get_content(typespec, Es)) of
+ case funcheader(TypeSpec) of
+ [] ->
+ signature(get_content(args, Es),
+ get_attrval(name, E));
+ Spec -> Spec
+ end
+ },
+ ?NL,{fsummary, fsummary(Es)},
+% ?NL,{type, local_types(TypeSpec)},
+ ?NL,local_types(TypeSpec),
+ ?NL,{desc, label_anchor(E)++fulldesc(Es)++sees(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 ->
+ 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.
+
+
+function_name(E) ->
+ get_attrval(name, E) ++ "/" ++ get_attrval(arity, E).
+
+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.
+
+%% <!ELEMENT args (arg*)>
+%% <!ELEMENT arg (argName, description?)>
+%% <!ELEMENT argName (#PCDATA)>
+
+%% This is currently only done for functions without type spec.
+
+signature(Es, Name) ->
+% [{tt, [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()", ?NL]}].
+ [Name, "("] ++ seq(fun arg/1, Es) ++ [") -> term()", ?NL].
+
+arg(#xmlElement{content = Es}) ->
+ [get_text(argName, Es)].
+
+%% <!ELEMENT typespec (erlangName, type, localdef*)>
+
+% typespec([]) -> [];
+% typespec(Es) ->
+% [{p, ([{tt, ([t_name(get_elem(qualifiedName, Es))]
+% ++ t_type(get_content(type, Es)))}]
+% ++ local_defs(get_elem(definition, Es)))},
+% ?NL].
+
+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, localdef(E)} || E <- Es]]}.
+
+%% <!ELEMENT typedecl (typedef, description?)>
+%% <!ELEMENT typedef (erlangName, argtypes, type?, localdef*)>
+
+types([]) -> [];
+types(Ts) ->
+ Es = lists:flatmap(fun ({Name, E}) -> typedecl(Name, E) end, Ts),
+ [?NL,
+% {h2, [{a, [{name, ?DATA_TYPES_LABEL}],
+% [?DATA_TYPES_TITLE]}]},
+% ?NL | Es]
+ {p,[{marker, [{id, ?DATA_TYPES_LABEL}],[]},
+ {em,[?DATA_TYPES_TITLE]}]},
+ ?NL, {taglist,[?NL|Es]}].
+
+%%type(Name, E=#xmlElement{content = Es}) ->
+%% ([?NL, {h3, label_anchor([Name, "()"], E)}, ?NL]
+%% ++ [{p, typedef(get_content(typedef, Es))}, ?NL]
+%% ++ fulldesc(Es)).
+typedecl(_Name, #xmlElement{content = Es}) ->
+ [{tag, typedef(get_content(typedef, Es))},?NL,{item,fulldesc(Es)},?NL].
+
+
+type_name(#xmlElement{content = Es}) ->
+ t_name(get_elem(erlangName, get_content(typedef, Es))).
+
+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
+ [] -> [{b, ["abstract datatype"]}, ": ", {tt, Name}];
+ Type ->
+ [{tt, Name ++ [" = "] ++ t_utype(Type)}]
+ end
+ ++ local_defs(get_elem(localdef, Es))).
+
+local_defs([]) -> [];
+local_defs(Es) ->
+ [?NL, {ul, [{li, [{tt, localdef(E)}]} || E <- Es]}].
+
+localdef(E = #xmlElement{content = Es}) ->
+ (case get_elem(typevar, Es) of
+ [] ->
+ label_anchor(t_abstype(get_content(abstype, Es)), E);
+ [V] ->
+ t_var(V)
+ end
+ ++ [" = "] ++ t_utype(get_elem(type, Es))).
+
+fulldesc(Es) ->
+ case get_content(fullDescription, get_content(description, Es)) of
+% [] -> [?NL];
+ [] -> index_desc(Es);
+% Desc -> [{p, Desc}, ?NL]
+ Desc ->
+ {Short,Long} = find_first_p(Desc,[]),
+ [?NL,{p,Short}|Long] ++[?NL]
+ end.
+
+find_first_p([#xmlElement{name=p}|_]=Long,Short) ->
+ {lists:reverse(Short),Long};
+find_first_p([H|T],Short) ->
+ find_first_p(T,[H|Short]);
+find_first_p([],Short) ->
+ {lists:reverse(Short),[]}.
+
+
+sees1(Es) ->
+ case get_elem(see, Es) of
+ [] -> [];
+ Es1 ->
+ {section,[{title,["See also"]},{p,seq(fun see/1, Es1, [])}]}
+ end.
+
+sees(Es) ->
+ case get_elem(see, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{em, ["See also:"]}, " "] ++ seq(fun see/1, Es1, ["."])},
+ ?NL]
+ end.
+
+see(E=#xmlElement{content = Es}) ->
+ see(E,Es).
+
+see(E, Es) ->
+ case get_attrval(href, E) of
+ "" -> Es;
+ Ref ->
+ case lists:reverse(Ref) of
+ "lmgs.ppa_"++Ppa ->
+ App = lists:reverse(Ppa),
+ [{seealso, [{marker, App++"_app"}], [App]},"(6)"];
+ "lmgs."++Dom ->
+ Mod = lists:reverse(Dom),
+ [{seealso, [{marker, Mod}], [Mod]},"(3)"];
+ _ ->
+ [{seealso, [{marker, Ref}], Es}]
+ end
+ end.
+
+equiv(Es) ->
+ case get_content(equiv, Es) of
+ [] -> ["\s"];
+ Es1 ->
+ case get_content(expr, Es1) of
+ [] -> [];
+ [Expr] ->
+% Expr1 = {tt, [Expr]},
+% Expr1 = {c, [Expr]},
+ Expr1 = [Expr],
+ Expr2 = case get_elem(see, Es1) of
+ [] ->
+ {c,Expr1};
+ [E=#xmlElement{}] ->
+% see(E,Expr1)
+ case get_attrval(href, E) of
+ "" ->
+ {c,Expr1};
+ Ref ->
+ {seealso, [{marker, Ref}], Expr1}
+ end
+ end,
+ [{p, ["Equivalent to ", Expr2, "."]}, ?NL]
+ end
+ end.
+
+% replace_minus_with_percent([$-|T]) ->
+% [$%|T];
+% replace_minus_with_percent([H|T]) ->
+% [H|replace_minus_with_percent(T)].
+
+copyright(Es) ->
+ case get_content(copyright, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, ["Copyright \251 " | Es1]}, ?NL]
+ end.
+
+version(Es) ->
+ case get_content(version, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["Version:"]}, " " | Es1]}, ?NL]
+ end.
+
+since(Es) ->
+ case get_content(since, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["Introduced in:"]}, " " | Es1]}, ?NL]
+ end.
+
+deprecated(Es, S) ->
+ Es1 = get_content(description, get_content(deprecated, Es)),
+ case get_content(fullDescription, Es1) of
+ [] -> [];
+ Es2 ->
+ [{p, [{b, ["This " ++ S ++ " is deprecated:"]}, " " | Es2]},
+ ?NL]
+ end.
+
+% behaviours(Es) ->
+% case get_elem(behaviour, Es) of
+% [] -> [];
+% Es1 ->
+% [{p, [{b, ["Behaviour:"]}, " "] ++ seq(fun behaviour/1, Es1, ["."])},
+% ?NL]
+% end.
+
+% behaviour(E=#xmlElement{content = Es}) ->
+% case get_attrval(href, E) of
+% "" -> [{tt, Es}];
+% Ref -> [{a, [{href, Ref}], [{tt, Es}]}]
+% end.
+
+authors(Es) ->
+ case get_elem(author, Es) of
+ [] -> [?NL,{aname,["\s"]},?NL,{email,["\s"]}];
+ Es1 -> [?NL|seq(fun author/1, Es1, [])]
+%
+% [{p, [{b, ["Authors:"]}, " "] ++ seq(fun author/1, Es1, ["."])},
+% ?NL]
+ end.
+
+
+%% <!ATTLIST author
+%% name CDATA #REQUIRED
+%% email CDATA #IMPLIED
+%% website CDATA #IMPLIED>
+
+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]}].
+
+% author(E=#xmlElement{}) ->
+% Name = get_attrval(name, E),
+% Mail = get_attrval(email, E),
+% URI = get_attrval(website, E),
+% (if Name == Mail ->
+% [{a, [{href, "mailto:" ++ Mail}],[{tt, [Mail]}]}];
+% true ->
+% if Mail == "" -> [Name];
+% true -> [Name, " (", {a, [{href, "mailto:" ++ Mail}],
+% [{tt, [Mail]}]}, ")"]
+% end
+% end
+% ++ if URI == "" -> [];
+% true -> [" [", {em, ["web site:"]}, " ",
+% {tt, [{a, [{href, URI}], [URI]}]}, "]"]
+% end).
+
+references(Es) ->
+ case get_elem(reference, Es) of
+ [] -> [];
+ Es1 ->
+ [{p, [{b, ["References"]},
+ {ul, [{li, C} || #xmlElement{content = C} <- Es1]}]},
+ ?NL]
+ end.
+
+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([E = #xmlElement{name = abstype, content = Es}]) ->
+ T = t_abstype(Es),
+% see(E,T);
+ case get_attrval(href, E) of
+ "" -> T;
+ % Ref -> [{seealso, [{marker, Ref}], T}]
+ _Ref -> T
+ end;
+t_type([#xmlElement{name = union, content = Es}]) ->
+ t_union(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_abstype(Es) ->
+% ([t_name(get_elem(qualifiedName, Es)), "("]
+% ++ seq(fun t_type_elem/1, get_elem(type, 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_par(Es) ->
+% T = t_type(get_content(type, Es)),
+% case get_elem(variable, Es) of
+% [] -> T;
+% [V0] -> case t_variable(V0) of
+% T -> T;
+% V -> V ++ ["::"] ++ T
+% end
+% end.
+
+% t_par_elem(#xmlElement{content = Es}) -> t_par(Es).
+
+t_union(Es) ->
+ seq(fun t_utype_elem/1, Es, " | ", []).
+
+seq(F, Es) ->
+ seq(F, Es, []).
+
+seq(F, Es, Tail) ->
+ seq(F, Es, ", ", Tail).
+
+seq(F, [E], _Sep, Tail) ->
+ F(E) ++ Tail;
+seq(F, [E | Es], Sep, Tail) ->
+ F(E) ++ [Sep] ++ seq(F, Es, Sep, Tail);
+seq(_F, [], _Sep, Tail) ->
+ Tail.
+
+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, [#xmlAttribute{name = Name} = A | As]) ->
+ [A | get_attr(Name, As)];
+get_attr(Name, [_ | As]) ->
+ get_attr(Name, As);
+get_attr(_, []) ->
+ [].
+
+get_attrval(Name, #xmlElement{attributes = As}) ->
+ case get_attr(Name, As) of
+ [#xmlAttribute{value = V}] ->
+ V;
+ [] -> ""
+ end.
+
+get_content(Name, Es) ->
+ case get_elem(Name, Es) of
+ [#xmlElement{content = Es1}] ->
+ Es1;
+ [] -> []
+ end.
+
+get_text(Name, Es) ->
+ case get_content(Name, Es) of
+ [#xmlText{value = Text}] ->
+ Text;
+ [] -> ""
+ end.
+
+% local_label(R) ->
+% "#" ++ R.
+
+xml(Title, CSS, Body) ->
+ {html, [?NL,
+ {head, [?NL,
+ {title, [Title]},
+ ?NL] ++ CSS},
+ ?NL,
+ {body, [{bgcolor, "white"}], Body},
+ ?NL]
+ }.
+
+%% ---------------------------------------------------------------------
+
+ type(E) ->
+ type(E, []).
+
+% type(E, Ds) ->
+% xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds),
+% ?HTML_EXPORT).
+ type(E, Ds) ->
+ xmerl:export_simple_content(t_utype_elem(E) ++ local_defs(Ds),
+ ?SGML_EXPORT).
+
+
+package(E=#xmlElement{name = package, content = Es}, Options) ->
+ Opts = init_opts(E, Options),
+ Name = get_text(packageName, Es),
+ Title = io_lib:fwrite("Package ~s", [Name]),
+ Desc = get_content(description, Es),
+% ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Body = ([?NL, {h1, [Title]}, ?NL]
+% ++ ShortDesc
+ ++ copyright(Es)
+ ++ deprecated(Es, "package")
+ ++ version(Es)
+ ++ since(Es)
+ ++ authors(Es)
+ ++ references(Es)
+ ++ sees(Es)
+ ++ FullDesc),
+ XML = xml(Title, stylesheet(Opts), Body),
+ xmerl:export_simple([XML], ?SGML_EXPORT, []).
+
+overview(E=#xmlElement{name = overview, content = Es}, Options) ->
+ Opts = init_opts(E, Options),
+ Title = get_text(title, Es),
+ Desc = get_content(description, Es),
+% ShortDesc = get_content(briefDescription, Desc),
+ FullDesc = get_content(fullDescription, Desc),
+ Body = ([?NL, {h1, [Title]}, ?NL]
+% ++ ShortDesc
+ ++ copyright(Es)
+ ++ version(Es)
+ ++ since(Es)
+ ++ authors(Es)
+ ++ references(Es)
+ ++ sees(Es)
+ ++ FullDesc),
+ XML = xml(Title, stylesheet(Opts), Body),
+ xmerl:export_simple([XML], ?SGML_EXPORT, []).