summaryrefslogtreecommitdiff
path: root/lib/xmerl/doc/examples/xmerl_test.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xmerl/doc/examples/xmerl_test.erl')
-rw-r--r--lib/xmerl/doc/examples/xmerl_test.erl522
1 files changed, 522 insertions, 0 deletions
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() ->
+ "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"DTD/xhtml1-strict.dtd\"><html>"
+ "<head><title>Doc Title</title><author>Ulf Wiger</author></head>"
+ "<h1>heading1</h1>"
+ "<p>This is a paragraph of text.</p>"
+ "<h2>heading2</h2>"
+ "<p>This is another paragraph.</p>"
+ "<table>"
+ "<thead><tr><td>head1</td><td>head2</td></tr></thead>"
+ "<tr><td>col11</td><td>col122</td></tr>"
+ "<tr><td>col21</td><td>col122</td></tr>"
+ "</table>"
+ "</html>".
+
+xml_namespace() ->
+ "<?xml version=\"1.0\"?>"
+ "<!-- initially, the default namespace is \"books\" -->"
+ "<book xmlns='urn:loc.gov:books' xmlns:isbn='urn:ISBN:0-395-36341-6'>"
+ "<title>Cheaper by the Dozen</title>"
+ "<isbn:number>1568491379</isbn:number>"
+ "<notes>"
+ "<!-- make HTML the default namespace for some comments -->"
+ "<p xmlns='urn:w3-org-ns:HTML'>"
+ "This is a <i>funny</i> book!"
+ "</p>"
+ "</notes>"
+ "</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.
+
+