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.
"
+ ""
+ "head1 | head2 |
"
+ "col11 | col122 |
"
+ "col21 | col122 |
"
+ "
"
+ "".
+
+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 head1 | "
+ "\n head2 | "
+ "\n
"
+ "\n "
+ "\n "
+ "\n col11 | "
+ "\n col122 | "
+ "\n
"
+ "\n "
+ "\n col21 | "
+ "\n col122 | "
+ "\n
"
+ "\n
"
+ "\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.