From 1a5796cd12061ebb21e7e51a0b7bdf05ed4786a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 2 Sep 2010 16:56:23 +0200 Subject: xmerl: Add doc/examples directory Needed by the test suite. --- lib/xmerl/doc/examples/Makefile | 61 +++ lib/xmerl/doc/examples/mkdocs.erl | 9 + lib/xmerl/doc/examples/sdocbook2xhtml.erl | 823 ++++++++++++++++++++++++++++++ lib/xmerl/doc/examples/test_html.erl | 225 ++++++++ lib/xmerl/doc/examples/xmerl_test.erl | 522 +++++++++++++++++++ lib/xmerl/doc/examples/xml/test.xml | 6 + lib/xmerl/doc/examples/xml/test2.xml | 8 + lib/xmerl/doc/examples/xml/test3.xml | 8 + lib/xmerl/doc/examples/xml/test4.xml | 9 + lib/xmerl/doc/examples/xml/test5.xml | 9 + lib/xmerl/doc/examples/xml/testdtd.dtd | 17 + lib/xmerl/doc/examples/xml/xmerl.xml | 523 +++++++++++++++++++ lib/xmerl/doc/examples/xml/xmerl_xs.xml | 541 ++++++++++++++++++++ lib/xmerl/doc/examples/xserl_test.erl | 85 +++ 14 files changed, 2846 insertions(+) create mode 100644 lib/xmerl/doc/examples/Makefile create mode 100644 lib/xmerl/doc/examples/mkdocs.erl create mode 100644 lib/xmerl/doc/examples/sdocbook2xhtml.erl create mode 100755 lib/xmerl/doc/examples/test_html.erl create mode 100644 lib/xmerl/doc/examples/xmerl_test.erl create mode 100755 lib/xmerl/doc/examples/xml/test.xml create mode 100755 lib/xmerl/doc/examples/xml/test2.xml create mode 100755 lib/xmerl/doc/examples/xml/test3.xml create mode 100755 lib/xmerl/doc/examples/xml/test4.xml create mode 100755 lib/xmerl/doc/examples/xml/test5.xml create mode 100755 lib/xmerl/doc/examples/xml/testdtd.dtd create mode 100755 lib/xmerl/doc/examples/xml/xmerl.xml create mode 100644 lib/xmerl/doc/examples/xml/xmerl_xs.xml create mode 100644 lib/xmerl/doc/examples/xserl_test.erl (limited to 'lib/xmerl/doc') diff --git a/lib/xmerl/doc/examples/Makefile b/lib/xmerl/doc/examples/Makefile new file mode 100644 index 0000000000..2768ee1985 --- /dev/null +++ b/lib/xmerl/doc/examples/Makefile @@ -0,0 +1,61 @@ +ERLC = erlc +EMULATOR = beam +EBIN = . +HTML = . +XML = ./xml +RM = rm -f + +# ---------------------------------------------------- +# Common Macros +# ---------------------------------------------------- +include ../../vsn.mk +VSN = $(XMERL_VSN) + + +MODULES = \ + xmerl_test \ + test_html \ + xserl_test \ + mkdocs \ + sdocbook2xhtml + + + +DOC_FILES = $(DOCS:%=$(HTML)/%.html) + +ERL_COMPILE_FLAGS += $(DEBUG) -I ../../include +warn_unused_wars +debug_info + +SUB_DIRECTORIES = + +#all: $(MODULES:%=$(EBIN)/%.$(EMULATOR)) xsm $(DOC_FILES) +all opt: $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(DOC_FILES) + +info: + @echo "DOC_FILES: $(DOC_FILES)" + +clean: +# @for d in $(SUB_DIRECTORIES); do \ +# cd $$d; $(MAKE) clean; \ +# done + $(RM) $(HTML)/*.html + $(RM) $(EBIN)/*.beam + $(RM) core *~ + +debug: xsmdebug $(DOC_FILES) + +xsm: + @for d in $(SUB_DIRECTORIES); do \ + cd $$d; $(MAKE); \ + done + +xsmdebug: + @for d in $(SUB_DIRECTORIES); do \ + cd $$d; $(MAKE) DEBUG=-Ddebug=1; \ + done + +$(HTML)/%.html: $(XML)/%.xml + erl -noshell -pa ../../ebin -run mkdocs run $< $@ -s erlang halt + +$(EBIN)/%.beam: %.erl + $(ERLC) $(ERL_COMPILE_FLAGS) -o $(EBIN) $< + diff --git a/lib/xmerl/doc/examples/mkdocs.erl b/lib/xmerl/doc/examples/mkdocs.erl new file mode 100644 index 0000000000..9faa12685b --- /dev/null +++ b/lib/xmerl/doc/examples/mkdocs.erl @@ -0,0 +1,9 @@ +-module(mkdocs). +-author('mikael.karlsson@creado.com'). + +-export([run/1]). + +run([InFile, OutFile])-> + {A,_}=xmerl_scan:file(InFile,[{fetch_fun, fun(DTDSpec,S) -> {ok,S} end}]), + B = sdocbook2xhtml:process_xml(A), + file:write_file(OutFile,[B]). diff --git a/lib/xmerl/doc/examples/sdocbook2xhtml.erl b/lib/xmerl/doc/examples/sdocbook2xhtml.erl new file mode 100644 index 0000000000..f96bf51963 --- /dev/null +++ b/lib/xmerl/doc/examples/sdocbook2xhtml.erl @@ -0,0 +1,823 @@ +%%%---------------------------------------------------------------------- +%%% File : sdocbook2xhtml.erl +%%% Description : Erlang XSLT like "stylesheet" for exporting +%%% Simplified Docbook XML to XHTML. +%%% +%%% Modules used : lists, io_lib, xmerl, xmerl_lib, xmerl_xs +%%% +%%%---------------------------------------------------------------------- + +-module(sdocbook2xhtml). +-author('mikael.karlsson@creado.com'). + + +-include("xmerl.hrl"). +-import(xmerl_lib, [markup/3,mapxml/2, foldxml/3, mapfoldxml/3]). +-import(xmerl_xs, [ xslapply/2, value_of/1, select/2, built_in_rules/2]). + +-export([ process_xml/1 ]). + +-export([abbrev/4, + abstract/4, + acronym/4, + address/4, + anchor/4, + appendix/4, + appendixinfo/4, + article/4, + articleinfo/4, + audiodata/4, + audioobject/4, + author/4, + authorgroup/4, + authorinitials/4, + bibliography/4, + bibliomixed/4, + bibliomisc/4, + bibliomset/4, + biblioset/4, + blockquote/4, + caption/4, + citetitle/4, + city/4, + colspec/4, + command/4, + computeroutput/4, + copyright/4, + corpauthor/4, + country/4, + date/4, + edition/4, + editor/4, + email/4, + emphasis/4, + entry/4, + example/4, + fax/4, + figure/4, + filename/4, + firstname/4, + footnote/4, + holder/4, + honorific/4, + imagedata/4, + imageobject/4, + informaltable/4, + inlinemediaobject/4, + isbn/4, + issn/4, + issuenum/4, + legalnotice/4, + lineage/4, + link/4, + literal/4, + itemizedlist/4, + listitem/4, + mediaobject/4, + member/4, + note/4, + orderedlist/4, + othercredit/4, + othername/4, + para/4, + phone/4, + phrase/4, + programlisting/4, + publishername/4, + quote/4, + replaceable/4, + revhistory/4, + revision/4, + revnumber/4, + revremark/4, + row/4, + section/4, + sectioninfo/4, + simplelist/4, + subtitle/4, + surname/4, + systemitem/4, + table/4, + tbody/4, + term/4, + tfoot/4, + tgroup/4, + thead/4, + title/4, + titleabbrev/4, + trademark/4, + ulink/4, + userinput/4, + variablelist/4, + varlistentry/4, + xref/4, + year/4 + ]). + + +xmlhead() -> "<\?xml version=\"1.0\" encoding=\"iso-8859-1\"\?>". +doctype() ->"\n". + +style() -> + "". + +process_xml(E)-> +%% lists:flatten(template( E )). + template( E ). + +%% article is the root element +template(E0 = #xmlElement{name=article})-> + + E = changetitle(E0), %% Add section numbering to titles + + [ xmlhead(), doctype(), + "" + "" + "", + value_of(select("articleinfo/title",E)), + "", + style(), + "" + "", +%% "\"no", + xslapply( fun template/1, select("articleinfo",E)), + process_toc(E), %% Insert toc between info and main part of article + xslapply( fun template/1, select("section",E)), + xslapply( fun template/1, select("appendix",E)), + ""]; + +template(E = #xmlElement{name=Name})-> + A = xslapply( fun template/1, E), + case catch + sdocbook2xhtml:Name(A, E#xmlElement.attributes, E#xmlElement.parents,E) + of + {'EXIT', {undef, _}} -> + A; + {'EXIT', Reason} -> + exit(Reason); + Res -> + Res + end; + +template(E) -> + built_in_rules( fun template/1, E). + +%% ------------------------------------------------------------------- +%% simple serialize tags + +abbrev(Data, Attrs, [{bibliomset,_}|_], E)-> + ["
", Data, "
"]; +abbrev(Data, Attrs, Parents, E)-> + markup("abbr",Attrs, Data). + + +abstract(Data, Attrs, Parents, E)-> + ["

Abstract

", Data, "
"]. + +acronym(Data, Attrs, Parents, E)-> + markup("acronym",Attrs, Data). + +address(Data, Attrs, Parents, E)-> + markup("address", Attrs, Data). + +anchor(Data, Attrs, Parents, E)-> + case find_attribute(id, Attrs) of + {value,ID} -> + ["", Data, ""]; + false -> + Data + end. + +appendix(Data, Attrs, Parents, E)-> + ["

Appendix

", Data]. + +appendixinfo(Data,_,_,_)-> + Data. + +article(Data, Attrs, Parents, E)-> + ["" + "\"no", + Data, + ""]. + +articleinfo(Data,_,_,_)-> + Data. + +audiodata(Data, Attrs, Parents, E)->Data. + +audioobject(_,_,_,_)-> + []. + +author(Data, Attrs, [{authorgroup,_} | _], E)-> + markup("dd", Attrs, Data); +author(Data, Attrs, Parents, E)-> + Data. + +authorgroup(Data,_,_,_)-> + ["
Author
",Data,"
"]. + +authorinitials(Data,_,_,_)-> Data. + +bibliography(Data, Attrs, Parents, E)-> + ["

Bibliography

" ,Data]. + +bibliomisc(Data,_,_,_)-> Data. +bibliomixed(Data,_,_,_)-> ["
",Data, "
"]. +bibliomset(Data,_,_,_)-> [Data, "
"]. + +biblioset(Data,_,_,_)-> Data. + +blockquote(Data, Attrs, Parents, E)-> + markup("blockquote",Attrs, Data). + +caption(Data, Attrs, Parents, E)-> Data. + +citetitle(Data,_,_,_)-> ["",Data,""]. + +city(Data,_,_,_)-> + Data. + +%% Fix Me is it "col" element in html? +colspec(_, Attrs,_,_)-> + []. + +command(Data,_,_,_)-> + ["", Data, ""]. + +computeroutput(Data,_,_,_)-> + ["", Data, ""]. + +copyright(Data,_,_,_)-> + [ "© ", Data]. + +corpauthor(Data,_,_,_)-> + Data. + +country(Data,_,_,_)-> + Data. + +date(Data,_,[{revision,_}|_],_)-> + ["", Data, ""]; +date(Data,_,_,_)-> + Data. + +edition(Data,_,_,_)-> + Data. + +editor(Data,_,_,_)-> + Data. + +email(Data,_,_,_)-> + ["",Data,""]. + +emphasis(Data, Attrs, Parents, E)-> + ["", Data, ""]. + +%% Cell in a table +entry(Data, Attrs, [{row,_}, {thead,_} | _], E)-> + ["", Data, ""]; +entry(Data, Attrs, Parents, E)-> + ["", Data, ""]. + +example(Data, Attrs, Parents, E)-> + ["
", Data, "
"]. + +fax(Data, Attrs, Parents, E)-> + ["
", Data, "
"]. + +%% May contain ulink to image, resolved by ulink type +figure(Data, _, _, _)-> + Data. + +filename(Data, _, _, _)-> + ["", Data, ""]. + +firstname(Data, _, _, _)-> + [Data , " " ]. + +footnote(Data, _, _, _)-> + Data. + +holder(Data, _, _, _)-> + [" ",Data]. + +honorific(Data, _, _, _)-> + Data. + +imagedata(Data, Attrs, Parents, E)-> + SRC = + case find_attribute(fileref, Attrs) of + {value,AS} -> + " src=" ++ AS ++ " "; + false -> + [] + end, + ALT = + case SRC of + [] -> + " alt=\"No image!\" "; + _ ->" alt=\"" ++ SRC ++ "\" " + end, + WIDTH = + case find_attribute(width, Attrs) of + false -> + []; + {value,A} ->" width=" ++ A ++ " " + end, + + [""]. + + +imageobject(Data, Attrs, Parents, E)-> + Data. + +informaltable(Data, Attrs, Parents, E)-> + ["", Data, "
"]. + + +inlinemediaobject(Data, Attrs, Parents, E)-> + Data. + +isbn(Data, Attrs, Parents, E)-> + Data. + +issn(Data, Attrs, Parents, E)-> + Data. + +issuenum(Data, Attrs, Parents, E)-> + Data. + +itemizedlist(Data, Attrs, Parents, _)-> + markup("ul", Attrs, Data). + +%keyword +%{ +% display: inline; +%} + +%keywordset +%{ +% display: inline; +%} + +legalnotice(Data, Attrs, Parents, _)-> + markup("small", Attrs, Data). + +lineage(Data, Attrs, Parents, _)-> + Data. + +%lineannotation +%{ +% display: inline; +%} + +% Hypertext link +link(Data, Attrs, Parents, _)-> + case find_attribute(linkend, Attrs) of + {value,LINK} -> + ["", Data, ""]; + false -> + Data + end. + +listitem(Data, Attrs, [{varlistentry,_} | _], E) -> + markup("dd", Attrs, Data); +listitem(Data, Attrs, Parents, _)-> + markup("li", Attrs, Data). + +literal(Data, Attrs, Parents, _)-> + markup("tt", Attrs, Data). + +%literallayout +%{ +% display: inline; +%} + +mediaobject(Data, Attrs, Parents, _)-> + Data. + +%% simplelist member +member(Data, Attrs, Parents, _)-> + [Data,"

"]. + +note(Data, Attrs, Parents, _)-> +["
NOTE", Data, "
"]. + +%objectinfo +%{ +% display: inline; +%} + +%option +%{ +% display: inline; +%} + +orderedlist(Data, Attrs, Parents, _)-> + markup("ol",Attrs,Data). + +%% Hmm otheraddr not in DTD +%otheraddr +%{ +% display: inline; +%} + +othercredit(Data, Attrs, Parents, _)->Data. + +othername(Data, Attrs, Parents, E)->Data. + +%% IGNORE +%pagenums +%{ +% display: inline; +%} + +para(Data, Attrs, [{listitem,_}|_], E)-> + Data; +para(Data, Attrs, [{note,_}|_], E)-> + Data; +para(Data, Attrs, Parents, E)-> + markup("p", Attrs, Data). + +phone(Data, Attrs, Parents, E)->Data. + +phrase(Data, Attrs, Parents, E)->Data. + +%pob +%{ +% display: inline; +%} + +%postcode +%{ +% display: inline; +%} + +%printhistory +%{ +% display: inline; +%} + +%procedure +%{ +% display: inline; +%} + +programlisting(Data, Attrs, Parents, E)-> +["
", Data, 
+"
"]. + +%pubdate +%{ +% display: inline; +%} + +%publisher +%{ +% display: inline; +%} + +publishername(Data, Attrs, Parents, E)-> + Data. + +quote(Data, Attrs, Parents, _)-> + markup("q", Attrs, Data). + +replaceable(Data, Attrs, Parents,_)-> + markup("i", Attrs, Data). + +revhistory(Data, Attrs, Parents,E)-> + {A,B,C} = case E#xmlElement.language of + "en" -> {"Revision history","Date","Comment"}; + "sv" -> {"Revisionshistoria","Datum","Kommentar"}; + _ ->{"lang is undefined","lang is undefined","lang is undefined"} + end, + + ["

",A,"

","" + "", Data, + "
Rev.",B,"",C,"
"]. + +revision(Data, Attrs, Parents,_)-> + markup("tr", Attrs, Data). + +revnumber(Data, Attrs, Parents,_)-> + markup("td", Attrs, Data). + +revremark(Data, Attrs, Parents,_)-> + markup("td", Attrs, Data). + +row(Data, Attrs, Parents, E)-> + markup("tr", Attrs, Data). + +section(Data, Attrs, Parents, E)-> + Data. + +sectioninfo(Data, Attrs, Parents, E)->Data. + +%sidebar +%{ +% display: block; +%} + +simplelist(Data, Attrs, Parents, E)-> + ["
", Data, "
"]. + + +%state +%{ +% display: inline; +%} + +%step +%{ +% display: inline; +%} + +%street +%{ +% display: inline; +%} + +%substeps +%{ +% display: inline; +%} + +subtitle(Data, Attrs, Parents, E)-> + ["

", Data, "

"]. + +surname(Data, Attrs, Parents, E)->Data. + +systemitem(Data, Attrs, Parents, E)-> + markup("b", Attrs, Data). + +table(Data, Attrs, Parents, E)-> + ["", + Data, "
"]. + +%% Fix me alot +tbody(Data, Attrs, Parents, E)-> + markup("tbody", Attrs, Data). +%{ +% display: table-row-group; +%} + +term(Data, Attrs, [{varlistentry,_} | _], E) -> + markup("dt", Attrs, Data). + + +%textobject +%{ +% display: inline; +%} + +tfoot(Data, Attrs, Parents, E)-> + markup("tfoot",Attrs, Data). + +%% Fixme alot +tgroup(Data, Attrs, Parents, E)-> + markup("colgroup", Attrs, Data). +%{ +% display: table; +%} + +thead(Data, Attrs, Parents, E)-> + markup("thead",Attrs, Data). +%{ +% display: table-row-group; +%} + +title(Data, Attrs, Parents, E)-> +%% io:fwrite("Parents ~p~n", [Parents]), + title1(Data, Attrs, Parents, E). + +title1(Data, Attrs, [{section,_}, {section,_}, {section,_}, + {section,_}, {section,_}, {appendix,_} | _], E) -> + ["
", Data, "
"]; +title1(Data, Attrs, [{section,_}, {section,_}, {section,_}, + {section,_}, {appendix,_} | _], E) -> + ["
", Data, "
"]; +title1(Data, Attrs, [{section,_}, {section,_}, {section,_}, + {apendix,_} | _], E) -> + ["

", Data, "

"]; +title1(Data, Attrs, [{section,_}, {section,_}, {appendix,_} | _], E) -> + ["

", Data, "

"]; +title1(Data, Attrs, [{section,_}, {appendix,_} | _], E) -> + ["

", Data, "

"]; +title1(Data, Attrs, [{appendix,_} | _], E) -> + ["

", Data, "

"]; + +title1(Data, Attrs, [{section,_}, {section,_}, {section,_}, + {section,_}, {section,_}, {section,_} | _], E) -> + ["
", Data, "
"]; +title1(Data, Attrs, [{section,_}, {section,_}, {section,_}, + {section,_}, {section,_} | _], E) -> + ["
", Data, "
"]; +title1(Data, Attrs, [{section,_}, {section,_}, {section,_}, + {section,_} | _], E) -> + ["
", Data, "
"]; +title1(Data, Attrs, [{section,C}, {section,B}, {section,A} | _], E) -> + {value, Id} = find_attribute(id,Attrs), + ["

", Data, "

"]; +title1(Data, Attrs, [{section,B}, {section,A} | _], E) -> + {value, Id} = find_attribute(id,Attrs), + ["

", Data, "

"]; +title1(Data, Attrs, [{section,A} | _], E) -> + {value, Id} = find_attribute(id,Attrs), + ["

", Data, "

"]; +title1(Data, Attrs, [{articleinfo,_} | _], E) -> + ["

", Data, "

"]; +title1(Data, Attrs, [{table,_} | _], E) -> + ["", Data, ""]; +title1(Data, Attrs, [{bibliomset,_} | _], E) -> + ["", Data, ""]; +title1(Data, Attrs, Parents, E)-> + ["

", Data, "

"]. + +titleabbrev(Data, Attrs, Parents, E)->[]. + +trademark(Data, Attrs, Parents, E)-> + [ Data, " ® "]. + +ulink(Data, Attrs, Parents, E)-> + case find_attribute(url, Attrs) of + {value,LINK} -> + ["", Data, ""]; + false -> + Data + end. + + +%% User input is Constant Bold +userinput(Data, Attrs, Parents, E)-> + ["", Data, ""]. + +variablelist(Data, Attrs, Parents, E)-> + markup("dl", Attrs, Data). + +varlistentry(Data, Attrs, Parents, E)->Data. + +%videodata +%{ +% display: inline; +%} + +%videoobject +%{ +% display: inline; +%} + +%volumenum +%{ +% display: inline; +%} + +xref(Data, Attrs, Parents, E)-> + case find_attribute(linkend, Attrs) of + {value,LINK} -> + [""]; + false -> + Data + end. + +year(Data, Attrs, Parents, E)->Data. + +%% ---------------------------------------------------------- +%% Utils find_attribute copied from Ulf Wigers xmerl distribution + +find_attribute(Name, Attrs) -> + case lists:keysearch(Name, #xmlAttribute.name, Attrs) of + {value, #xmlAttribute{value = V}} -> + {value, V}; + false -> + false + end. +%% ------------ + +changetitle(A) -> + Afun = fun changecount/2, + {E, Acc} = mapfoldxml(Afun, {0,0,0,0,0,0}, A), + E. + +changecount(#xmlElement{name=title}=E, {A,B,C,Ex,Fig,Tab})-> + case E#xmlElement.parents of + [{example,_} |_] -> + {addexhead(E,{A,Ex+1}), {A,B,C,Ex+1,Fig,Tab} }; + [{figure,_} |_] -> + {addfighead(E,{A,Fig+1}), {A,B,C,Ex,Fig+1,Tab} }; + [{table,_} |_] -> + {addtablehead(E,{A,Tab+1}), {A,B,C,Ex,Fig,Tab+1} }; + [{section,_},{section,_},{section,_},{article,_} |_] -> + {addheader(E,{A,B,C+1}), {A,B,C+1,Ex,Fig,Tab} }; + [{section,_},{section,_},{article,_} |_] -> + { addheader(E,{A,B+1,0}), {A,B+1,0,Ex,Fig,Tab} }; + [{section,_},{article,_} |_] -> + {addheader(E,{A+1,0,0}),{A+1,0,0,0,0,0}}; + _ -> + {E,{A,B,C,Ex,Fig,Tab}} + end; +changecount(E, Acc)->{E,Acc}. + +addexhead(#xmlElement{name=title,content=[#xmlText{}=T1|_]}= E, {Ch,No})-> + NewHeader = "Example " ++ + integer_to_list(Ch)++" - "++ integer_to_list(No) ++ + " " ++ T1#xmlText.value, + E#xmlElement{content=[T1#xmlText{value=NewHeader}]}. +addfighead(#xmlElement{name=title,content=[#xmlText{}=T1|_]}= E, {Ch,No})-> + NewHeader = "Figure " ++ + integer_to_list(Ch)++" - "++ integer_to_list(No) ++ + " " ++ T1#xmlText.value, + E#xmlElement{content=[T1#xmlText{value=NewHeader}]}. +addtablehead(#xmlElement{name=title,content=[#xmlText{}=T1|_]}= E, {Ch,No})-> + NewHeader = "Table " ++ + integer_to_list(Ch)++" - "++ integer_to_list(No) ++ + " " ++ T1#xmlText.value, + E#xmlElement{content=[T1#xmlText{value=NewHeader}]}. + +addheader(#xmlElement{name=title,content=[#xmlText{}=T1|_]}= E, Chapters)-> + NewHeader = chapterstring(Chapters)++ " " ++ T1#xmlText.value, + NewAtts = addid(E#xmlElement.attributes, Chapters), + E#xmlElement{content=[T1#xmlText{value=NewHeader}], + attributes = NewAtts}. + +chapterstring({A,0,0})->integer_to_list(A); +chapterstring({A,B,0})->integer_to_list(A)++"."++ integer_to_list(B); +chapterstring({A,B,C})->integer_to_list(A) ++ "." ++ + integer_to_list(B) ++ "." ++ + integer_to_list(C). + +%% addid add id attribute if it not already exists +addid(OldAtts, Chapters)-> + case find_attribute(id, OldAtts) of + {value,_} -> + OldAtts; + false -> + add_attribute(id,"sect_"++ chapterstring(Chapters), + OldAtts) + end. + +add_attribute(Name, Value, OldAtts)-> + [#xmlAttribute{ name=Name, value = Value}| OldAtts ]. + + +process_toc(E)-> + AFun = fun chapindex/2, + TOCR = foldxml(AFun, [], E), +% Str = case find_attribute(lang, E#xmlElement.attributes) of +% {value,"en"} -> "Table of Contents"; +% {value,"sv"} -> "Innehållsförtecking"; +% _ ->"lang is undefined" +% end, + Str = case E#xmlElement.language of + "en" -> "Table of Contents"; + "sv" -> "Innehållsförtecking"; + _ ->"lang is undefined" + end, + TOC = ["

",Str,"

    ", +lists:reverse(TOCR), "
"]. + +chapindex(#xmlElement{name=title}=E, Accu)-> + case E#xmlElement.parents of + [{section,_},{section,_},{section,_},{article,_} |_] -> + ["
  • "++spind(3)++ addlink(E,"toc_level_3") ++"
  • "| Accu]; + [{section,_},{section,_},{article,_} |_] -> + ["
  • "++spind(2)++ addlink(E,"toc_level_2") ++ "
  • "| Accu]; + [{section,_},{article,_} |_] -> + ["
  • "++spind(1)++ addlink(E,"toc_level_1") ++"
  • "| Accu]; + _ -> + Accu + end; +chapindex(E, Accu) -> + Accu. + +spind(0) ->""; +spind(X)-> + "  " ++ spind(X-1). + +addlink(E, TocLevel)-> + {value,LINK} = find_attribute(id,E#xmlElement.attributes), + [#xmlText{value=Title}|_] = E#xmlElement.content, %% Pfuii + "
    " ++ + Title ++ "". + + diff --git a/lib/xmerl/doc/examples/test_html.erl b/lib/xmerl/doc/examples/test_html.erl new file mode 100755 index 0000000000..3ca15f30f8 --- /dev/null +++ b/lib/xmerl/doc/examples/test_html.erl @@ -0,0 +1,225 @@ +%%% The contents of this file are subject to the Erlang Public License, +%%% Version 1.0, (the "License"); you may not use this file except in +%%% compliance with the License. You may obtain a copy of the License at +%%% http://www.erlang.org/license/EPL1_0.txt +%%% +%%% Software distributed under the License is distributed on an "AS IS" +%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%%% the License for the specific language governing rights and limitations +%%% under the License. +%%% +%%% The Original Code is xmerl-0.7 +%%% +%%% The Initial Developer of the Original Code is Ericsson Telecom +%%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson +%%% Telecom AB. All Rights Reserved. +%%% +%%% Contributor(s): ______________________________________. +%%% +%%%---------------------------------------------------------------------- +%%% #0. BASIC INFORMATION +%%%---------------------------------------------------------------------- +%%% File: test_html.erl +%%% Author : Ulf Wiger + +%%% Description : Callback module for exporting XML to HTML with support +%%% for special Erlang-related tags. (Experimental) +%%% +%%% Modules used : lists, io_lib +%%% +%%%---------------------------------------------------------------------- + +-module(test_html). +-author('ulf.wiger@ericsson.com'). + + +-export(['#xml-inheritance#'/0]). + +%%% special Erlang forms +-export(['EXIT'/4, + 'tuple_list'/4]). + +-export(['#root#'/4, + title/4, + heading/4, + section/4, + table/4, + row/4, + col/4, + data/4, + p/4, para/4, 'P'/4, + emphasis/4]). + +-include("xmerl.hrl"). + + +'#xml-inheritance#'() -> [xmerl_xml]. + + + +%% The '#root#' tag is called when the entire structure has been exported. +%% It does not appear in the structure itself. +'#root#'(Data, Attrs, [], E) -> + Title = + case find_attribute(title, Attrs) of + {value, T} -> + ["", T, ""]; + false -> + [] + end, + ["\n" + "\n", + "\n", Title, "\n" + "\n", Data, "\n"]. + + + + +%%% Special token: EXIT +'EXIT'(Reason, Attrs = [], Parents = [], E) -> + %% This happens e.g. if a request function crashes completely. + ["
    \n", mk_string({'EXIT', Reason}), "
    "]. + + +title(Str, Attrs, Parents, E) -> + ["

    ", Str, "

    \n"]. + + +%%% section/3 is to be used instead of headings. +section(Data, Attrs, [{section,_}, {section,_}, {section,_} | _], E) -> + opt_heading(Attrs, "

    ", "

    ", Data); +section(Data, Attrs, [{section,_}, {section,_} | _], E) -> + opt_heading(Attrs, "

    ", "

    ", Data); +section(Data, Attrs, [{section,_} | _], E) -> + opt_heading(Attrs, "

    ", "

    ", Data); +section(Data, Attrs, Parents, E) -> + opt_heading(Attrs, "

    ", "

    ", Data). + +opt_heading(Attrs, StartTag, EndTag, Data) -> + case find_attribute(heading, Attrs) of + {value, Text} -> + [StartTag, Text, EndTag, "\n" | Data]; + false -> + Data + end. + + +%% tables +%% e.g. {table, [{heading, [{col, H1}, {col, H2}]}, +%% {row, [{col, C11}, {col, C12}]}, +%% {row, [{col, C21}, {col, C22}]}]}. + +table(Data, Attrs, Parents, E) -> + Border = case find_attribute(border, Attrs) of + false -> + " border=1"; + {value, N} -> + [" border=", mk_string(N)] + end, + ["\n", Data, "\n\n"]. + +row(Data, Attrs, [{table,_}|_], E) -> + ["", Data, "\n"]. + +heading(Data, Attrs, [{table,_}|_], E) -> + ["", Data, "\n"]. + + +%% Context-sensitive columns (heading- or row columns) +col(Data, Attrs, [{heading,_}, {table,_} | _], E) -> + ["", nbsp_if_empty(Data), "\n"]; +col(Data, Attrs, [{row,_}, {table,_} | _], E) -> + ["", nbsp_if_empty(Data), "\n"]. + + +tuple_list(List, Attrs, Parents, E) -> + Elems = case find_attribute(elements, Attrs) of + {value, Es} -> + Es; + false -> + case List of + [H|_] -> + lists:seq(1,size(H)); + [] -> + [] + end + end, + TableData = [{row, [{col, {element(P, Rec)}} || P <- Elems]} || + Rec <- List], + Table = case find_attribute(heading, Attrs) of + {value, Cols} -> + Head = {heading, [{col, C} || C <- Cols]}, + {table, [Head | TableData]}; + false -> + {table, TableData} + end, + {'#xml-redefine#', Table}. + + +data(Data, Pos, Attrs, Parents) -> + mk_string(Data). + + + +p(Data, Pos, Attrs, Parents) -> + {'#xml-alias#', 'P'}. + +para(Data, Pos, Attrs, Parents) -> + {'#xml-alias#', 'P'}. + +'P'(Data, Pos, Attrs, Parents) -> + ["

    ", mk_string(Data), "

    \n"]. + + +emphasis(Str, Pos, Attrs, Parents) -> + ["", Str, ""]. + + +nbsp_if_empty(Data) when binary(Data), size(Data) == 0 -> + " "; +nbsp_if_empty(Data) when list(Data) -> + case catch list_to_binary(Data) of + {'EXIT', _} -> + nbsp_if_empty_term(Data); + B when size(B) == 0 -> + " "; + _ -> + Data + end; +nbsp_if_empty(Data) -> + nbsp_if_empty_term(Data). + +nbsp_if_empty_term(Data) -> + Str = io_lib:format("~p", [Data]), + case list_to_binary(Str) of + B when size(B) == 0 -> + " "; + _ -> + Str + end. + + +mk_string(I) when integer(I) -> + integer_to_list(I); +mk_string(A) when atom(A) -> + atom_to_list(A); +mk_string(L) when list(L) -> + %% again, we can't regognize a string without "parsing" it + case catch list_to_binary(L) of + {'EXIT',_} -> + io_lib:format("~p", [L]); + _ -> + L + end; +mk_string(Term) -> + io_lib:format("~p", [Term]). + + + +find_attribute(Name, Attrs) -> + case lists:keysearch(Name, #xmlAttribute.name, Attrs) of + {value, #xmlAttribute{value = V}} -> + {value, V}; + false -> + false + end. diff --git a/lib/xmerl/doc/examples/xmerl_test.erl b/lib/xmerl/doc/examples/xmerl_test.erl new file mode 100644 index 0000000000..b4288431f2 --- /dev/null +++ b/lib/xmerl/doc/examples/xmerl_test.erl @@ -0,0 +1,522 @@ +-module(xmerl_test). + +-compile(export_all). +%%-export([Function/Arity, ...]). + +-define(XMERL_APP,). + +-include("xmerl.hrl"). + +%% Export to HTML from "simple" format +test1() -> + xmerl:export_simple(simple(), xmerl_html, [{title, "Doc Title"}]). + + +%% Export to XML from "simple" format +test2() -> + xmerl:export_simple(simple(), xmerl_xml, [{title, "Doc Title"}]). + + +%% Parse XHTML, and export result to HTML and text +test3() -> + FetchFun = fun(_DTDSpec, S) -> {ok, not_fetched,S} end, + {A, _} = xmerl_scan:string(html(), + [{fetch_fun,FetchFun}]), + io:format("From xmerl_scan:string/2~n ~p~n", [A]), + B = xmerl:export([A], xmerl_html), + io:format("From xmerl:export/2 xmerl_html filter~n ~p~n", [B]), + C = xmerl:export([A], xmerl_text), + io:format("From xmerl:export/2 xmerl_text filter~n ~p~n", [C]). + + +test4() -> + FetchFun = fun(_DTDSpec, S) -> {ok, not_fetched, S} end, + {A,_} = xmerl_scan:string(xml_namespace(), + [{fetch_fun,FetchFun}, + {namespace_conformant,true}]), + io:format("From xmerl_scan:string/2~n ~p~n", [A]). + +test5() -> + {ok, Cwd} = file:get_cwd(), % Assume we are in the examples dir... + File = Cwd ++ "/xml/xmerl.xml", + FetchFun = fun(_DTDSpec, S) -> {ok, not_fetched, S} end, +% {Resp0,Rest0}=xmerl_eventp:stream(File,[]), +% io:format("Tree: ~p~n",[Resp0]), + {Resp1, _Rest1}=xmerl_eventp:file_sax(File, ?MODULE, undefined, + [{fetch_fun, FetchFun}]), + io:format("Using file_sax: counted ~p paragraphs~n", [Resp1]), + {Resp2, _Rest2} = xmerl_eventp:stream_sax(File, ?MODULE, undefined, []), + io:format("Using stream_sax: counted ~p paragraphs~n", [Resp2]). + +test6() -> + FetchFun = fun(_DTDSpec, S) -> {ok, {string,""}, S} end, + {Doc, _} = xmerl_scan:string(xml_namespace(), + [{fetch_fun, FetchFun}, + {namespace_conformant, true}]), + E = xmerl_xpath:string("child::title[position()=1]", Doc), + io:format("From xmerl_scan:string/2~n E=~p~n", [E]). + + +simple() -> + [{document, + [{title, ["Doc Title"]}, + {author, ["Ulf Wiger"]}, + {section,[{heading, ["heading1"]}, + {'P', ["This is a paragraph of text."]}, + {section,[{heading, ["heading2"]}, + {'P', ["This is another paragraph."]}, + {table,[{border, ["1"]}, + {heading,[{col, ["head1"]}, + {col, ["head2"]}]}, + {row, [{col, ["col11"]}, + {col, ["col12"]}]}, + {row, [{col, ["col21"]}, + {col, ["col22"]}]} + ]} + ]} + ]} + ]} + ]. + + +html() -> + "" + "Doc TitleUlf Wiger" + "

    heading1

    " + "

    This is a paragraph of text.

    " + "

    heading2

    " + "

    This is another paragraph.

    " + "" + "" + "" + "" + "
    head1head2
    col11col122
    col21col122
    " + "". + +xml_namespace() -> + "" + "" + "" + "Cheaper by the Dozen" + "1568491379" + "" + "" + "

    " + "This is a funny book!" + "

    " + "
    " + "
    ". + + +%%% ============================================================================ +%%% Generic callbacks + +%'#text#'(Text) -> +% []. + +'#root#'(Data, Attrs, [], _E) -> + io:format("root... Data=~p Attrs=~p E=~p~n",[Data,Attrs,_E]), + []. + +'#element#'(Tag, Data, Attrs, _Parents, _E) -> + io:format("Tag=~p~n Data=~p~n Attrs=~p~n Parents=~p~n E=~p~n", + [Tag, Data, Attrs, _Parents, _E]), + []. + +'#element#'(_Tag, _Data, _Attrs, CBstate) -> +% io:format("Tag=~p~n Data=~p~n Attrs=~p~n CBstate=~p~n", +% [Tag, Data, Attrs, CBstate]), + CBstate. + +'#text#'(Text, CBstate) -> + io:format("Text=~p~n CBstate=~p~n", + [Text, CBstate]), + CBstate. + + +'#xml-inheritance#'() -> + [xmerl_html]. + + + + +%%% ============================================================================ +%%% To run these tests you must first download the testsuite from www.w3c.org +%%% xmlconf.xml is the main test file that contains references to all the tests. +%%% Thus parse this, export result and execute tests in the call-back functions. +%%% Note: +%%% - xmerl assumes all characters are represented with a single integer. +w3cvalidate() -> + Tests = filename:join(filename:dirname(filename:absname(code:which(xmerl))), + "../w3c/xmlconf/xmlconf.xml"), + TestDir = filename:dirname(Tests), + io:format("Looking for W3C tests at ~p~n", [Tests]), + {ok, Bin} = file:read_file(Tests), + +% String = ucs:to_unicode(binary_to_list(Bin), 'utf-8'), +% case xmerl_scan:string(String, [{xmlbase, TestDir}]) of + case xmerl_scan:string(binary_to_list(Bin), [{xmlbase, TestDir}]) of + {error, Reason} -> + io:format("ERROR xmerl:scan_file/2 Reason=~w~n", [Reason]); + {A, _Res} -> +% io:format("From xmerl:scan_file/2 ~n A=~p~n Res=~w~n", [A,Res]), + C = xmerl:export([A], xmerl_test), + io:format("From xmerl:export/2 xmerl_text filter~n ~p~n", [C]) + end. + + +'TESTSUITE'(_Data, Attrs, _Parents, _E) -> + _Profile = find_attribute('PROFILE', Attrs), +% io:format("testsuite Profile=~p~n", [Profile]), + []. + +'TESTCASES'(_Data, Attrs, _Parents, _E) -> + Profile = find_attribute('PROFILE', Attrs), + XMLbase = find_attribute('xml:base', Attrs), + io:format("testsuite Profile=~p~n xml:base=~p~n", [Profile, XMLbase]), + []. + +%% More info on Canonical Forms can be found at: +%% http://dev.w3.org/cvsweb/~checkout~/2001/XML-Test-Suite/xmlconf/sun/cxml.html?content-type=text/html;%20charset=iso-8859-1 +'TEST'(Data, Attrs, _Parents, E) -> +% io:format("test Attrs=~p~n Parents=~p~n E=~p~n",[Attrs, _Parents, E]), + Id = find_attribute('ID', Attrs), + io:format("Test: ~p ",[Id]), + Entities = find_attribute('ENTITIES', Attrs), % Always handle all entities + Output1 = find_attribute('OUTPUT', Attrs), % + Output3 = find_attribute('OUTPUT3', Attrs), % FIXME! + Sections = find_attribute('SECTIONS', Attrs), + Recommendation = find_attribute('RECOMMENDATION', Attrs), % FIXME! + Type = find_attribute('TYPE', Attrs), % Always handle all entities + Version = find_attribute('VERSION', Attrs), % FIXME! + URI = find_attribute('URI', Attrs), + Namespace = find_attribute('NAMESPACE', Attrs), % FIXME! + + OutputForm= + if + Output1 =/= undefined -> Output1; + true -> Output3 + end, + Test = filename:join(E#xmlElement.xmlbase, URI), +% io:format("TEST URI=~p~n E=~p~n",[Test,E]), + case Type of + "valid" -> +% io:format("Data=~p~n Attrs=~p~n Parents=~p~n Path=~p~n", +% [Data, Attrs, _Parents, Test]), + test_valid(Test, Data, Sections, Entities, OutputForm, Recommendation, + Version, Namespace); + "invalid" -> + test_invalid(Test, Data, Sections, Entities, OutputForm, Recommendation, + Version, Namespace); + "not-wf" -> + test_notwf(Test, Data, Sections, Entities, OutputForm, Recommendation, + Version, Namespace); + "error" -> + test_error(Test, Data, Sections, Entities, OutputForm, Recommendation, + Version, Namespace) + end, + []. + +%% Really basic HTML font tweaks, to support highlighting +%% some aspects of test descriptions ... +'EM'(Data, _Attrs, _Parents, _E) -> + [$" |Data ++ [$"]]. + +'B'(Data, _Attrs, _Parents, _E) -> + [$" |Data ++ [$"]]. + + + +find_attribute(Tag,Attrs) -> + case xmerl_lib:find_attribute(Tag, Attrs) of + {value, Id} -> Id; + false -> undefined + end. + + +-define(CONT, false). + +%%% All parsers must accept "valid" testcases. +test_valid(URI, Data, Sections, Entities, OutputForm, Recommendation, Version, + Namespace) -> + io:format("nonvalidating ", []), + case nonvalidating_parser_q(URI) of + {Res, Tail} when is_record(Res, xmlElement) -> + case is_whitespace(Tail) of + true -> + io:format("OK ", []), + ok; + false -> + print_error({Res, Tail}, URI, Sections, Entities, OutputForm, + Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end; + Error -> + print_error(Error, URI, Sections, Entities, OutputForm, Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end, + io:format("validating ", []), + case validating_parser_q(URI) of + {Res2, Tail2} when is_record(Res2, xmlElement) -> + case is_whitespace(Tail2) of + true -> + io:format("OK~n", []), + ok; + false -> + print_error({Res2, Tail2}, URI, Sections, Entities, OutputForm, + Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end; + Error2 -> + print_error(Error2, URI, Sections, Entities, OutputForm, Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end. + + +%%% Nonvalidating parsers must accept "invalid" testcases, but validating ones +%%% must reject them. +test_invalid(URI, Data, Sections, Entities, OutputForm, Recommendation, Version, + Namespace) -> + io:format("nonvalidating ", []), + case nonvalidating_parser_q(URI) of + {Res,Tail} when is_record(Res, xmlElement) -> + case is_whitespace(Tail) of + true -> + io:format("OK ", []), + ok; + false -> + print_error({Res, Tail}, URI, Sections, Entities, OutputForm, + Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end; + Error -> + print_error(Error, URI, Sections, Entities, OutputForm, Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end, + io:format("validating ", []), + case validating_parser_q(URI) of + {Res2, Tail2} when is_record(Res2, xmlElement) -> + case is_whitespace(Tail2) of + false -> + io:format("OK~n", []), + ok; + true -> + print_error({Res2, Tail2}, URI, Sections, Entities, OutputForm, + Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end; + {error, enoent} -> + print_error("Testfile not found", URI, Sections, Entities, OutputForm, + Recommendation, Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end; + _Error2 -> + io:format("OK~n", []), + ok + end. + +%%% No parser should accept a "not-wf" testcase unless it's a nonvalidating +%%% parser and the test contains external entities that the parser doesn't read +test_notwf(URI, Data, Sections, Entities, OutputForm, Recommendation, Version, + Namespace) -> + io:format("nonvalidating ", []), + case nonvalidating_parser_q(URI) of + {Res, Tail} when is_record(Res, xmlElement) -> + case is_whitespace(Tail) of + false -> + io:format("OK ", []), + ok; + true -> + print_error({Res, Tail}, URI, Sections, Entities, OutputForm, + Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end; + {error,enoent} -> + print_error("Testfile not found",URI,Sections,Entities,OutputForm, + Recommendation,Version,Namespace,Data), + if + ?CONT==false -> throw({'EXIT', failed_test}); + true -> error + end; + _Error -> + io:format("OK ",[]), + ok + end, + io:format("validating ",[]), + case validating_parser_q(URI) of + {Res2, Tail2} when is_record(Res2, xmlElement) -> + case is_whitespace(Tail2) of + false -> + io:format("OK~n", []), + ok; + true -> + print_error({Res2, Tail2}, URI, Sections, Entities, OutputForm, + Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end; + {error,enoent} -> + print_error("Testfile not found", URI, Sections, Entities, OutputForm, + Recommendation, Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end; + _Error2 -> + io:format("OK~n", []), + ok + end. + +%%% Parsers are not required to report "errors", but xmerl will always... +test_error(URI, Data, Sections, Entities, OutputForm, Recommendation, Version, + Namespace) -> + io:format("nonvalidating ", []), + case nonvalidating_parser_q(URI) of + {'EXIT', _Reason} -> + io:format("OK ", []), + ok; + {error, enoent} -> + print_error("Testfile not found", URI, Sections, Entities, OutputForm, + Recommendation, Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end; + Res -> + print_error(Res, URI, Sections, Entities, OutputForm, Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end, + io:format("validating ", []), + case validating_parser_q(URI) of + {'EXIT', _Reason2} -> + io:format("OK~n", []), + ok; + {error, enoent} -> + print_error("Testfile not found", URI, Sections, Entities, OutputForm, + Recommendation, Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end; + Res2 -> + print_error(Res2, URI, Sections, Entities, OutputForm, Recommendation, + Version, Namespace, Data), + if + ?CONT == false -> throw({'EXIT', failed_test}); + true -> error + end + end. + + +%%% Use xmerl as nonvalidating XML parser +nonvalidating_parser(URI) -> + (catch xmerl_scan:file(URI, [])). + + +%%% Use xmerl as nonvalidating XML parser +nonvalidating_parser_q(URI) -> + (catch xmerl_scan:file(URI, [{quiet, true}])). + + +%%% Use xmerl as validating XML parser +validating_parser(URI) -> + (catch xmerl_scan:file(URI, [{validation, true}])). + + +%%% Use xmerl as validating XML parser +validating_parser_q(URI) -> + (catch xmerl_scan:file(URI, [{validation, true}, {quiet, true}])). + + +is_whitespace([]) -> + true; +is_whitespace([H |Rest]) when ?whitespace(H) -> + is_whitespace(Rest); +is_whitespace(_) -> + false. + + +print_error(Error, URI, Sections, Entities, OutputForm, Recommendation, Version, + Namespace, Data) -> + io:format("ERROR ~p~n URI=~p~n See Section ~s~n",[Error, URI, Sections]), + if + Entities == undefined -> ok; + true -> io:format(" Entities =~s~n",[Entities]) + end, + if + OutputForm == undefined -> ok; + true -> io:format(" OutputForm=~s FIXME!~n",[OutputForm]) + end, + if + Recommendation == undefined -> ok; + true -> io:format(" Recommendation=~s~n",[Recommendation]) + end, + if + Version == undefined -> ok; + true -> io:format(" Version =~s~n",[Version]) + end, + if + Namespace == undefined -> ok; + true -> io:format(" Namespace =~s~n",[Namespace]) + end, + io:format(Data). + + + + + + + + + +%%% ============================================================================ +%%% Callbacks for parsing of Simplified DocBook XML + +para(_Data, _Attrs, US) -> + case US of + Int when is_integer(Int) -> Int+1; + undefined -> 1 + end. + + diff --git a/lib/xmerl/doc/examples/xml/test.xml b/lib/xmerl/doc/examples/xml/test.xml new file mode 100755 index 0000000000..e803a83560 --- /dev/null +++ b/lib/xmerl/doc/examples/xml/test.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/xmerl/doc/examples/xml/test2.xml b/lib/xmerl/doc/examples/xml/test2.xml new file mode 100755 index 0000000000..0cb11194fc --- /dev/null +++ b/lib/xmerl/doc/examples/xml/test2.xml @@ -0,0 +1,8 @@ + + + + This is a comment + + + + diff --git a/lib/xmerl/doc/examples/xml/test3.xml b/lib/xmerl/doc/examples/xml/test3.xml new file mode 100755 index 0000000000..dbdc1e62c2 --- /dev/null +++ b/lib/xmerl/doc/examples/xml/test3.xml @@ -0,0 +1,8 @@ + + + + This is a comment + + + + diff --git a/lib/xmerl/doc/examples/xml/test4.xml b/lib/xmerl/doc/examples/xml/test4.xml new file mode 100755 index 0000000000..e9d85b8d8f --- /dev/null +++ b/lib/xmerl/doc/examples/xml/test4.xml @@ -0,0 +1,9 @@ + + + + + This is a comment + + + + diff --git a/lib/xmerl/doc/examples/xml/test5.xml b/lib/xmerl/doc/examples/xml/test5.xml new file mode 100755 index 0000000000..e9d85b8d8f --- /dev/null +++ b/lib/xmerl/doc/examples/xml/test5.xml @@ -0,0 +1,9 @@ + + + + + This is a comment + + + + diff --git a/lib/xmerl/doc/examples/xml/testdtd.dtd b/lib/xmerl/doc/examples/xml/testdtd.dtd new file mode 100755 index 0000000000..2ce1c513a6 --- /dev/null +++ b/lib/xmerl/doc/examples/xml/testdtd.dtd @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/lib/xmerl/doc/examples/xml/xmerl.xml b/lib/xmerl/doc/examples/xml/xmerl.xml new file mode 100755 index 0000000000..f02282dbef --- /dev/null +++ b/lib/xmerl/doc/examples/xml/xmerl.xml @@ -0,0 +1,523 @@ + + + +
    + + XMerL - XML processing tools for Erlang + Reference Manual + + + Ulf + Wiger + + + + + 1.02003-02-04 + Converted xml from html + + + + XMerL tools contains xmerl_scan; a non-validating XML + processor, xmerl_xpath; a XPath implementation, xmerl for export + of XML trees to HTML, XML or text and xmerl_xs for XSLT like + transforms in erlang. + + + + +
    + xmerl_scan - the XML processor + The (non-validating) XML processor is activated through + xmerl_scan:string/[1,2] or + xmerl_scan:file/[1,2]. + It returns records of the type defined in xmerl.hrl. + + + As far as I can tell, xmerl_scan implements the complete XML + 1.0 spec, including: + + entity expansion + fetching and parsing external DTDs + contitional processing + UniCode + XML Names + + +xmerl_scan:string(Text [ , Options ]) -> #xmlElement{}. +xmerl_scan:file(Filename [ , Options ]) -> #xmlElement{}. + + The Options are basically to specify the behaviour of the + scanner. See the source code for details, but you can specify + funs to handle scanner events (event_fun), process the document + entities once identified (hook_fun), and decide what to do if the + scanner runs into eof before the document is complete + (continuation_fun). + + You can also specify a path (fetch_path) as a list of + directories to search when fetching files. If the file in question + is not in the fetch_path, the URI will be used as a file + name. + + +
    + Customization functions + The XML processor offers a number of hooks for + customization. These hooks are defined as function objects, and + can be provided by the caller. + + The following customization functions are available. If + they also have access to their own state variable, the access + function for this state is identified within parentheses: + + + + event function ( + xmerl_scan:event_state/[1,2] + ) + + hook function ( + xmerl_scan:hook_state/[1,2] + ) + + fetch function ( + xmerl_scan:fetch_state/[1,2] ) + + + continuation function ( + xmerl_scan:cont_state/[1,2] ) + + + rules function ( + xmerl_scan:rules_state/[1,2] ) + + + accumulator function + + close function + + + + For all of the above state access functions, the function + with one argument + (e.g. event_fun(GlobalState)) + will read the state variable, while the function with two + arguments (e.g.: event_fun(NewStateData, + GlobalState)) will modify it. + + For each function, the description starts with the syntax + for specifying the function in the + Options list. The general forms + are {Tag, Fun}, or + {Tag, Fun, LocalState}. The + second form can be used to initialize the state variable in + question. + +
    + User State + + All customization functions are free to access a + "User state" variable. Care must of course be taken + to coordinate the use of this state. It is recommended that + functions, which do not really have anything to contribute to + the "global" user state, use their own state + variable instead. Another option (used in + e.g. xmerl_eventp.erl) is for + customization functions to share one of the local states (in + xmerl_eventp.erl, the + continuation function and the fetch function both acces the + cont_state.) + + Functions to access user state: + + + + + xmerl_scan:user_state(GlobalState) + + + xmerl_scan:user_state(UserState', + GlobalState) + + + +
    +
    + Event Function + + {event_fun, fun()} | {event_fun, fun(), + LocalState} + + The event function is called at the beginning and at the + end of a parsed entity. It has the following format and + semantics: + + + + EventState = xmerl_scan:event_state(GlobalState), + EventState' = foo(Event, EventState), + GlobalState' = xmerl_scan:event_state(EventState', GlobalState) +end. +]]> + +
    +
    + Hook Function + {hook_fun, fun()} | {hook_fun, fun(), + LocalState} + + + +The hook function is called when the processor has parsed a complete +entity. Format and semantics: + + + + HookState = xmerl_scan:hook_state(GlobalState), + {TransformedEntity, HookState'} = foo(Entity, HookState), + GlobalState' = xmerl_scan:hook_state(HookState', GlobalState), + {TransformedEntity, GlobalState'} +end. +]]> + + The relationship between the event function, the hook + function and the accumulator function is as follows: + + + The event function is first called with an + 'ended' event for the parsed entity. + + The hook function is called, possibly + re-formatting the entity. + + The acc function is called in order to + (optionally) add the re-formatted entity to the contents of + its parent element. + + + +
    +
    + Fetch Function + +{fetch_fun, fun()} | {fetch_fun, fun(), LocalState} + +The fetch function is called in order to fetch an external resource +(e.g. a DTD). + +The fetch function can respond with three different return values: + + + + +Format and semantics: + + + + FetchState = xmerl_scan:fetch_state(GlobalState), + Result = foo(URI, FetchState). % Result being one of the above +end. +]]> + +
    +
    + Continuation Function + +{continuation_fun, fun()} | {continuation_fun, fun(), LocalState} + +The continuation function is called when the parser encounters the end +of the byte stream. Format and semantics: + + + + ContState = xmerl_scan:cont_state(GlobalState), + {Result, ContState'} = get_more_bytes(ContState), + GlobalState' = xmerl_scan:cont_state(ContState', GlobalState), + case Result of + [] -> + GlobalState' = xmerl_scan:cont_state(ContState', GlobalState), + Exception(GlobalState'); + MoreBytes -> + {MoreBytes', Rest} = end_on_whitespace_char(MoreBytes), + ContState'' = update_cont_state(Rest, ContState'), + GlobalState' = xmerl_scan:cont_state(ContState'', GlobalState), + Continue(MoreBytes', GlobalState') + end +end. +]]> +
    +
    + Rules Functions + + +{rules, ReadFun : fun(), WriteFun : fun(), LocalState} | +{rules, Table : ets()} + + The rules functions take care of storing scanner + information in a rules database. User-provided rules functions + may opt to store the information in mnesia, or perhaps in the + user_state(LocalState). + + The following modes exist: + + + + If the user doesn't specify an option, the + scanner creates an ets table, and uses built-in functions to + read and write data to it. When the scanner is done, the ets + table is deleted. + + If the user specifies an ets table via the + {rules, Table} option, the + scanner uses this table. When the scanner is done, it does + not delete the table. + + If the user specifies read and write + functions, the scanner will use them instead. + + + + The format for the read and write functions are as + follows: + + + + NewScannerState. +ReadFun(Context, Name, ScannerState) -> Definition | undefined. +]]> + + Here is a summary of the data objects currently being + written by the scanner: + + + Scanner data objects + + + + Context + Key Value + Definition + + + + + notation + NotationName + {system, SL} | {public, PIDL, SL} + + + elem_def + ElementName + #xmlElement{content = ContentSpec} + + + parameter_entity + PEName + PEDef + + + entity + EntityName + EntityDef + + + +
    + + + + + When <Elem> is not wrapped with +<Occurrence>, (Occurrence = once) is implied. + +
    +
    + Accumulator Function + {acc_fun, fun()} | {acc_fun, fun(), + LocalState} + + The accumulator function is called to accumulate the + contents of an entity.When parsing very large files, it may + not be desireable to do so.In this case, an acc function can + be provided that simply doesn't accumulate. + + Note that it is possible to even modify the parsed + entity before accumulating it, but this must be done with + care. xmerl_scan performs + post-processing of the element for namespace management. Thus, + the element must keep its original structure for this to + work. + + The acc function has the following format and + semantics: + + + + {[X|Acc], GlobalState}. + +%% non-accumulating acc fun +fun(ParsedEntity, Acc, GlobalState) -> + {Acc, GlobalState}. +]]> +
    +
    + Close Function + + The close function is called when a document (either the + main document or an external DTD) has been completely + parsed. When xmerl_scan was started using + xmerl_scan:file/[1,2], the + file will be read in full, and closed immediately, before the + parsing starts, so when the close function is called, it will + not need to actually close the file. In this case, the close + function will be a good place to modify the state + variables. + + Format and semantics: + + + + GlobalState' = .... % state variables may be altered +]]> +
    + +
    + +
    + +
    + XPATH + + + + [DocEntity] + +DocEntity : #xmlElement{} + | #xmlAttribute{} + | #xmlText{} + | #xmlPI{} + | #xmlComment{} +]]> + + The xmerl_xpath module does seem to handle the entire XPATH + 1.0 spec, but I haven't tested that much yet. The grammar is + defined in + xmerl_xpath_parse.yrl. The core + functions are defined in + xmerl_xpath_pred.erl. +
    +
    + Some useful shell commands for debugging the XPath parser + + + -1")). +xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("5 * 6 div 2")). +xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("5 + 6 mod 2")). +xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("5 * 6")). +xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("5 * 6")). +xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("-----6")). +xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("parent::node()")). +xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("descendant-or-self::node()")). +xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("parent::processing-instruction('foo')")).]]> +
    +
    + Erlang Data Structure Export + + The idea as follows: + + The Erlang data structure should look like this: + + + + Some short forms are allowed: + + {Tag, [], Content} +Tag -> {Tag, [], []} +]]> + + Note that content lists must be flat, but strings can be + deep. + + It is also allowed to include normal + #xml... elements in the simple + format. + + xmerl:export_simple(Data, + Callback) takes the above data structure and + exports it, using the callback module + Callback. + + The callback module should contain hook functions for all + tags present in the data structure. The hook function must have + the format: + Tag(Data, Attrs, Parents, E) + + + where E is an #xmlElement{} + record (see xmerl.hrl). + + Attrs is converted from the simple [{Key, + Value}] to + [#xmlAttribute{}] + + Parents is a list of [{ParentTag, + ParentTagPosition}]. + + The hook function should return either the Data to be + exported, or the tuple {'#xml-redefine#', + NewStructure}, where + NewStructure is an element (which + can be simple), or a (simple-) content list wrapped in a 1-tuple + as {NewContent}. + + The callback module can inherit definitions from other + callback modules, through the required function + '#xml-interitance#() -> + [ModuleName]. + + As long as a tag is represented in one of the callback + modules, things will work. It is of course also possible to + redefine a tag. +
    + XSLT like transforms + See separate document xmerl_xs.html + . +
    +
    + +
    diff --git a/lib/xmerl/doc/examples/xml/xmerl_xs.xml b/lib/xmerl/doc/examples/xml/xmerl_xs.xml new file mode 100644 index 0000000000..9a798808b9 --- /dev/null +++ b/lib/xmerl/doc/examples/xml/xmerl_xs.xml @@ -0,0 +1,541 @@ + + + +
    + + XSLT like transformations in Erlang + User Guide + + + Mikael + Karlsson + + + + + 1.02002-10-25 + First Draft + + + 1.12003-02-05 + Moved module xserl to xmerl application, renamed to + xmerl_xs + + + + Erlang has similarities to XSLT since both languages + have a functional programming approach. Using the xpath implementation + in the existing xmerl application it is possible to write XSLT + like transforms in Erlang. One can also combine the + transformations with the erlang scripting possibility + in the yaws webserver to implement "on the fly" html + conversions of xml documents. + + + + + +
    + Terminology + + + XML + + Extensible Markup Language + + + + XSLT + + Extensible Stylesheet Language: Transformations + + + +
    +
    + Introduction + XSLT stylesheets are often used when transforming XML + documents, to other XML documents or (X)HTML for presentation. + There are a number of brick-sized books written on the + topic. XSLT contains quite many + functions and learning them all may take some effort, which + could be a reason why the author only has reached a basic level of + understanding. This document assumes a basic level of + understanding of XSLT. + + Since XSLT is based on a functional programming approach + with pattern matching and recursion it is possible to write + similar style sheets in Erlang. At least for basic + transforms. XPath which is used in XSLT is also already + implemented in the xmerl application written i Erlang. This + document describes how to use the XPath implementation together + with Erlangs pattern matching and a couple of functions to write + XSLT like transforms. + This approach is probably easier for an Erlanger but + if you need to use real XSLT stylesheets in order to "comply to + the standard" there is an adapter available to the Sablotron + XSLT package which is written i C++. + + + This document is written in the Simplified Docbook DTD which is + a subset of the complete one and converted to xhtml using a + stylesheet written in Erlang. + +
    + +
    + Tools +
    + xmerl + xmerl + is a xml parser written in Erlang +
    + xmerl_xpath + XPath is in important part of XSLT and is implemented in + xmerl +
    +
    + xmerl_xs + + xmerl_xs is a very small + module acting as "syntactic sugar" for the XSLT lookalike + transforms. It uses xmerl_xpath. + +
    +
    + +
    + yaws + + Yaws, Yet Another + Webserver, is a web server written in Erlang that support dynamic + content generation using embedded scripts, also written in Erlang. + + + Yaws is not needed to make the XSLT like transformations, but + combining yaws and xmerl it is possible to do transformations + of XML documents to HTML in realtime, when clients requests a + web page. As an example I am able to edit this document using + emacs with psgml tools, save the document and just do a reload + in my browser to see the result. The parse/transform time is not + visually different compared to loading any other document in the + browser. + +
    + +
    + +
    + Transformations + + When xmerl_scan parses an xml string/file it returns a record of: + + + + + + Were content is a mixed list of yet other xmlElement records and/or + xmlText (or other node types). + +
    + xmerl_xs functions + + Functions used: + + + + xslapply/2 + + function to make things look similar + to xsl:apply-templates. + + + + + value_of/1 + + Conatenates all text nodes within a tree. + + + + select/2 + + select(Str, E) extracts nodes from the XML tree using + xmerl_xpath. + + + + + built_in_rules/2 + + The default fallback behaviour, template funs should + end with: + template(E)->built_in_rules(fun + template/1, E). + + + + + +Text is escaped using xmerl_lib:export_text/1 for + "<", ">" and other relevant xml + characters when exported. So the value_of/1 and built_in_rules/2 + functions should be replaced when not exporting to xml or html. + +
    + + +
    Examples + + Using xslapply + original XSLT: + + +

    + +

    + + ]]> +
    + + becomes in Erlang: + + + ["

    ", + xslapply(fun template/1, E), + "

    "]; + ]]> +
    + +
    + + Using value_of and select + + +

    + + ]]> +
    + + becomes: + + + + ["

    ", value_of(select(".", E)), "

    "]; + ]]> +
    +
    + + Simple xsl stylesheet + + A complete example with the XSLT sheet in the xmerl distribution. + + + + + + + + + + + + <xsl:value-of select="title"/> + + + + + + + + + +

    + +

    +
    + + +

    + +

    +
    + + +

    + +

    +
    + + +

    + +

    +
    + + +

    + NOTE: + +

    +
    + + + + + + + + + ]]> +
    +
    + + Erlang version + + Erlang transformation of previous example: + + + + "". + +process_xml(Doc)-> + template(Doc). + +template(E = #xmlElement{name='doc'})-> + [ "<\?xml version=\"1.0\" encoding=\"iso-8859-1\"\?>", + doctype(), + "" + "" + "", value_of(select("title",E)), "" + "" + "", + xslapply( fun template/1, E), + "" + "" ]; + + +template(E = #xmlElement{ parents=[{'doc',_}|_], name='title'}) -> + ["

    ", + xslapply( fun template/1, E), + "

    "]; + +template(E = #xmlElement{ parents=[{'chapter',_}|_], name='title'}) -> + ["

    ", + xslapply( fun template/1, E), + "

    "]; + +template(E = #xmlElement{ parents=[{'section',_}|_], name='title'}) -> + ["

    ", + xslapply( fun template/1, E), + "

    "]; + +template(E = #xmlElement{ name='para'}) -> + ["

    ", xslapply( fun template/1, E), "

    "]; + +template(E = #xmlElement{ name='note'}) -> + ["

    " + "NOTE: ", + xslapply( fun template/1, E), + "

    "]; + +template(E = #xmlElement{ name='emph'}) -> + ["", xslapply( fun template/1, E), ""]; + +template(E)-> + built_in_rules( fun template/1, E). + ]]> +
    + + It is important to end with a call to + xmerl_xs:built_in_rules/2 + if you want any text to be written in "push" transforms. + That are the ones using a lot xslapply( fun + template/1, E ) instead of + value_of(select("xpath",E)), + which is pull... + +
    +The largest example is the stylesheet to transform this document + from the Simplified Docbook XML format to xhtml. The source + file is sdocbook2xhtml.erl. + +
    +
    + Tips and tricks +
    + for-each + The function for-each is quite common in XSLT stylesheets. + It can often be rewritten and replaced by select/1. Since + select/1 returns a list of #xmlElements and xslapply/2 + traverses them it is more or less the same as to loop over all + the elements. + +
    +
    + position() + The XSLT position() and #xmlElement.pos are not the + same. One has to make an own position in Erlang. + + Counting positions + + +

    + + + +    +
    +
    + ]]> +
    +Can be written as + + + {Lines,LineNo} = lists:mapfoldl(fun template_pos/2, 1, select("line", E)), + ["

    ", Lines, "

    "]. + +template_pos(E = #xmlElement{name='line'}, P) -> + {[indent_line(P rem 2), value_of(E#xmlElement.content), "
    "], P + 1 }. + +indent_line(0)->"  "; +indent_line(_)->"". + ]]> +
    +
    +
    +
    + Global tree awareness + In XSLT you have "root" access to the top of the tree + with XPath, even though you are somewhere deep in your + tree. + The xslapply/2 function only carries back the child part + of the tree to the template fun. But it is quite easy to write + template funs that handles both the child and top tree. + + Passing the root tree + The following example piece will prepend the article + title to any section title + + + ["

    ", value_of(select("title", ETop))," - ", + xslapply( fun(A) -> template(A, ETop) end, E), + "

    "]; + ]]> +
    +
    +
    +
    + +
    + + +
    + Utility functions + + The module xmerl_xs contains the functions + mapxml/2, foldxml/3 and + mapfoldxml/3 to traverse + #xmlElement trees. They can be used in order + to build cross-references, see sdocbook2xhtml.erl for instance + where foldxml/3 and + mapfoldxml/3 are used to + number chapters, examples and figures and to build the Table of + contents for the document. + +
    + + +
    + Future enhancements + + More wish- than task-list at the moment. + + + + More stylesheets + + + On the fly exports to PDF for printing and also more + "polished" presentations. + + + +
    + +
    + References + + + XML source + file for this document. + + + + Erlang style + sheet used for this document. (Simplified Docbook DTD). + + + Open Source Erlang + + + + +
    +
    + + diff --git a/lib/xmerl/doc/examples/xserl_test.erl b/lib/xmerl/doc/examples/xserl_test.erl new file mode 100644 index 0000000000..69db75cfe8 --- /dev/null +++ b/lib/xmerl/doc/examples/xserl_test.erl @@ -0,0 +1,85 @@ +-module(xserl_test). +-include("xmerl.hrl"). +-import(xserl,[ xslapply/2, value_of/1, select/2, built_in_rules/2 ]). +-export([process_xml/1,test/0]). + +doctype()-> + "". + +test() -> + Str= "" + "" + "Cheaper by the Dozen" + "1568491379" + "" + "" + "

    " + "This is a funny book!" + "

    " + "
    " + "
    ", + {Doc,_}=xmerl_scan:string(Str,[{fetch_fun, fun(DTDSpec,S) -> {ok,S} end}]), + + process_xml(Doc). + +process_xml(Doc)-> + template(Doc). + +template(E = #xmlElement{name='doc'})-> + [ "<\?xml version=\"1.0\" encoding=\"iso-8859-1\"\?>", + doctype(), + "" + "" + "", xserl:value_of(select("title",E)), "" + "" + "", + xslapply( fun template/1, E), + "" + "" ]; + + +template(E = #xmlElement{ parents=[{'doc',_}|_], name='title'}) -> + ["

    ", +%% xslapply( fun template/1, E), +%% same as + lists:map( fun template/1, E#xmlElement.content ), + "

    "]; + +template(E = #xmlElement{ parents=[{'chapter',_}|_], name='title'}) -> + ["

    ", + xslapply( fun template/1, E), + "

    "]; + +template(E = #xmlElement{ parents=[{'section',_}|_], name='title'}) -> + ["

    ", + xslapply( fun template/1, E), + "

    "]; + +template(E = #xmlElement{ name='para'}) -> + ["

    ", + xslapply( fun template/1, E), + "

    "]; + +template(E = #xmlElement{ name='note'}) -> + ["

    " + "NOTE: ", + xslapply( fun template/1, E), + "

    "]; + +template(E = #xmlElement{ name='emph'}) -> + ["", + xslapply( fun template/1, E), + ""]; + +template(E)-> + built_in_rules( fun template/1, E). + +%% It is important to end with a call to xserl:built_in_rules/2 +%% if you want any text to be written in "push" transforms. +%% That are the ones using a lot xslapply( fun template/1, E ) +%% instead of value_of(select("xpath",E)), which is pull... +%% Could maybe be caught as an exception in xslapply instead, +%% but I think that could degrade performance - having an +%% exception for every #xmlText element. + -- cgit v1.2.3