diff --git a/lib/xmerl/doc/src/xmerl_ug.xmlsrc b/lib/xmerl/doc/src/xmerl_ug.xmlsrc index b3fcbf093005..8f96bc23c9e1 100644 --- a/lib/xmerl/doc/src/xmerl_ug.xmlsrc +++ b/lib/xmerl/doc/src/xmerl_ug.xmlsrc @@ -11,7 +11,7 @@ Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + xmerl @@ -171,7 +171,7 @@ Grand Danois\

If you want to parse the XML file motorcycles.xml you run it in the Erlang shell like:

-3> {ParsResult,Misc}=xmerl_scan:file("motorcycles.xml"). 
+3> {ParseResult,Misc}=xmerl_scan:file("motorcycles.xml").
 {{xmlElement,motorcycles,
              motorcycles,
              [],
@@ -279,7 +279,7 @@ Grand Danois\
       Name = atom()
       Value = IOString | atom() | integer()
     
-    

See also reference manual for +

See also reference manual for xmerl

If you want to add the information about a black Harley Davidsson 1200 cc Sportster motorcycle from 2003 that is in @@ -359,6 +359,18 @@ Data = ...

The result will be:

+

+ + The generated XML above was formatted for readability. + + Another exporter which indents the code with 2 spaces can also be used. + + In order to use it one only needs to change the export-module:

+
+      ...
+        Export=xmerl:export_simple([NewRootEl],xmerl_xml_indent,[{prolog,Prolog}]),
+      ...
+    
@@ -488,4 +500,3 @@ template(E = #xmlElement{name='bike'}) -> elements and the 'manufacturer' elements are not in order.

- diff --git a/lib/xmerl/src/Makefile b/lib/xmerl/src/Makefile index e7e7c8e978b8..ff84b7de8d2a 100644 --- a/lib/xmerl/src/Makefile +++ b/lib/xmerl/src/Makefile @@ -77,6 +77,7 @@ MODULES = $(EDOC_MODULES) \ xmerl_validate \ xmerl_xlate \ xmerl_xml \ + xmerl_xml_indent \ xmerl_xpath_lib \ xmerl_xpath_parse \ xmerl_xpath_pred \ diff --git a/lib/xmerl/src/xmerl.app.src b/lib/xmerl/src/xmerl.app.src index aed9cf176f9b..f3ab13ee264f 100644 --- a/lib/xmerl/src/xmerl.app.src +++ b/lib/xmerl/src/xmerl.app.src @@ -27,6 +27,7 @@ xmerl_validate, xmerl_xlate, xmerl_xml, + xmerl_xml_indent, xmerl_xpath, xmerl_xpath_lib, xmerl_xpath_parse, diff --git a/lib/xmerl/src/xmerl_xml_indent.erl b/lib/xmerl/src/xmerl_xml_indent.erl new file mode 100644 index 000000000000..deb9f0890e3a --- /dev/null +++ b/lib/xmerl/src/xmerl_xml_indent.erl @@ -0,0 +1,86 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% Description : Callback module for exporting complete or simple forms to indented XML. +%% +%% This module indents the xml with 2 spaces and a newline \n. +%% Currently the implementation does not allow it to be configured. +%% The implementation is based on the same Elixir implementation. +%% https://hexdocs.pm/xmerl_xml_indent/readme.html + +-module(xmerl_xml_indent). + +-export(['#xml-inheritance#'/0]). + +-export(['#root#'/4, + '#element#'/5, + '#text#'/1]). + +-import(xmerl_lib, [markup/3, empty_tag/2, export_text/1]). + +-include("xmerl.hrl"). +-include("xmerl_internal.hrl"). + + +'#xml-inheritance#'() -> []. + + +%% The '#text#' function is called for every text segment. + +'#text#'(Text) -> + export_text(Text). + + +%% The '#root#' tag is called when the entire structure has been +%% exported. It does not appear in the structure itself. + +'#root#'(Data, [#xmlAttribute{name=prolog,value=V}], [], _E) -> + [V,Data]; +'#root#'(Data, _Attrs, [], _E) -> + ["\n", Data]. + + +%% The '#element#' function is the default handler for XML elements. + +'#element#'(Tag, [], Attrs, _Parents, _E) -> + empty_tag(Tag, Attrs); +'#element#'(Tag, Data, Attrs, Parents, _E) -> + IsCharData = is_char(Data), + NewData = + case IsCharData of + true -> + LengthParents = length(Parents), + %% Push all the data over Lvl spaces. + [ + indent(LengthParents + 1) ++ DataEntry + || DataEntry <- Data + ] ++ indent(LengthParents); + false -> + Data + end, + markup(Tag, Attrs, NewData). + +is_char([[X|_]|_]) -> + not is_integer(X); +is_char(Data) when is_list(Data) -> + false. + +indent(Level) -> + [$\n | lists:duplicate(2 * Level, $\s)]. diff --git a/lib/xmerl/test/xmerl_SUITE.erl b/lib/xmerl/test/xmerl_SUITE.erl index cf1728bf2ad4..0d01b98b3714 100644 --- a/lib/xmerl/test/xmerl_SUITE.erl +++ b/lib/xmerl/test/xmerl_SUITE.erl @@ -40,14 +40,14 @@ %%---------------------------------------------------------------------- %% Test groups %%---------------------------------------------------------------------- -all() -> +all() -> [{group, cpd_tests}, xpath_text1, xpath_main, xpath_abbreviated_syntax, xpath_functions, xpath_namespaces, {group, misc}, {group, eventp_tests}, {group, ticket_tests}, {group, app_test}, - {group, appup_test}]. + {group, appup_test}, {group, format_test}]. -groups() -> +groups() -> [{cpd_tests, [], [cpd_invalid1, cpd_invalid1_index, cpd_invalid2_index, cpd_invalid_index3, cpd_invalid_is_layer, @@ -63,7 +63,8 @@ groups() -> ticket_6873, ticket_7496, ticket_8156, ticket_8697, ticket_9411, ticket_9457, ticket_9664_schema, ticket_9664_dtd]}, {app_test, [], [{xmerl_app_test, all}]}, - {appup_test, [], [{xmerl_appup_test, all}]}]. + {appup_test, [], [{xmerl_appup_test, all}]}, + {format_test, [], [formatter_pass,formatter_fail]}]. suite() -> [{timetrap,{minutes,10}}]. @@ -257,12 +258,12 @@ xml_ns(_Config) -> attributes = [#xmlAttribute{name = 'xmlns:xml', expanded_name = {"xmlns","xml"}, nsinfo = {"xmlns","xml"}, - namespace = #xmlNamespace{default = [], + namespace = #xmlNamespace{default = [], nodes = [{"xml",'http://www.w3.org/XML/1998/namespace'}]}}, #xmlAttribute{name = 'xml:attr1', expanded_name = {'http://www.w3.org/XML/1998/namespace',attr1}, nsinfo = {"xml","attr1"}, - namespace = #xmlNamespace{default = [], + namespace = #xmlNamespace{default = [], nodes = [{"xml",'http://www.w3.org/XML/1998/namespace'}]}}]}, [] } = xmerl_scan:string(Doc2, [{namespace_conformant, true}]), @@ -349,15 +350,15 @@ sax_parse_export_xml_small(Config) -> ok. simple() -> - [{document, + [{document, [{title, "Doc Title"}, {author, "Ulf Wiger"}], - [{section, + [{section, [{heading, "heading1"}], [{'P', ["This is a paragraph of text."]}, - {section, + {section, [{heading, "heading2"}], [{'P', ["This is another paragraph."]}, - {table, + {table, [{border, 1}], [{heading, [{col, ["head1"]}, @@ -393,7 +394,7 @@ generate_section_attribute(0) -> generate_section_attribute(N) -> {{heading, "heading1"},N-1}. - + generate_subsection_content(0) -> done; generate_subsection_content(1) -> @@ -450,7 +451,7 @@ generate_heading_col(N) -> ticket_5998(Config) -> DataDir = datadir(Config), %% First fix is tested by case syntax_bug2. - + ok = case catch xmerl_scan:file(filename:join([DataDir,misc,"ticket_5998_2.xml"])) of {'EXIT',{fatal,Reason1}} -> @@ -484,18 +485,18 @@ ticket_7211(Config) -> {E,[]} = xmerl_scan:file(filename:join([DataDir,misc,"notes2.xml"]), [{fetch_path,[filename:join([DataDir,misc,erlang_docs_dtd])]}, {validation,dtd}]), - + ok = case E of Rec when is_record(Rec,xmlElement) -> ok; _ -> E end, - + {E2,[]} = xmerl_scan:file(filename:join([DataDir,misc,"XS.xml"]), [{fetch_path,[filename:join([DataDir,misc,erlang_docs_dtd])]}, {validation,dtd}]), - + ok = case E2 of Rec2 when is_record(Rec2,xmlElement) -> ok; @@ -517,7 +518,7 @@ ticket_7214(Config) -> {E,[]} = xmerl_scan:file(filename:join([DataDir,misc,'block_tags.html']), [{validation,dtd}, {fetch_path,[filename:join([DataDir,misc,erlang_docs_dtd])]}]), - + ok = case E of Rec when is_record(Rec,xmlElement) -> ok; @@ -528,7 +529,7 @@ ticket_7214(Config) -> %% %% ticket_7430 %% -%% Problem with contents of numeric character references followed by +%% Problem with contents of numeric character references followed by %% UTF-8 characters.. %% ticket_7430(_Config) -> @@ -631,7 +632,7 @@ allow_entities_test(Config) -> DataDir = proplists:get_value(data_dir, Config), File = filename:join(DataDir, "lol_1_test.xml"), %% Depth 9 %% Disallow entities - {'EXIT',{fatal, {{error,entities_not_allowed}, _, _, _}}} = + {'EXIT',{fatal, {{error,entities_not_allowed}, _, _, _}}} = (catch xmerl_scan:file(File, [{allow_entities, false}])), ok. @@ -679,7 +680,7 @@ change_mode3([F|Fs]) -> chmod(F) end, change_mode3(Fs). - + chmod(F) -> case file:read_file_info(F) of {ok,FileInfo} -> @@ -696,3 +697,101 @@ datadir(Config) -> datadir_join(Config,Files) -> filename:join([datadir(Config)|Files]). + +%%====================================================================== +%% New formatter tests input/output +%%====================================================================== + +html() -> + "" + "Doc TitleUlf Wiger" + "

heading1

" + "

This is a paragraph of text.

" + "

heading2

" + "

This is another paragraph.

" + "" + "" + "" + "" + "
head1head2
col11col122
col21col122
" + "". + +html_indented() -> + "" + "\n" + "\n " + "\n Doc Title" + "\n Ulf Wiger" + "\n " + "\n

heading1

" + "\n

This is a paragraph of text.

" + "\n

heading2

" + "\n

This is another paragraph.

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

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

" + "
" + "
". + +xml_namespace_indented() -> + "" + "\n" + "\n Cheaper by the Dozen" + "\n 1568491379" + "\n " + "\n

This is a funny book!

" + "\n
" + "\n
". + +output_element_to_str(E) -> + Output = xmerl:export([E], xmerl_xml_indent), + [Str] = io_lib:format("~s", [lists:flatten(Output)]), + Str. + +%%====================================================================== +%% New formatter tests +%%====================================================================== +formatter_pass(_Config) -> + + FetchFun = fun(_DTDSpec, S) -> {ok, not_fetched, S} end, + %% Generate based on namespace-example + {Ns, _} = xmerl_scan:string(xml_namespace(), [{fetch_fun, FetchFun}]), + GNs = output_element_to_str(Ns), + INs = xml_namespace_indented(), + INs = GNs, + + %% Generate based on html-example + {Html, _} = xmerl_scan:string(html(), [{fetch_fun, FetchFun}]), + GHtml = output_element_to_str(Html), + IHtml = html_indented(), + GHtml = IHtml, + ok. + +formatter_fail(_Config) -> + ok.