1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2003-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%% Description  : Functions to export simple and complete XML forms
23%%
24
25%% @doc Functions for exporting XML data to an external format.
26%%
27
28-module(xmerl).
29
30%-compile(export_all).
31
32-export([export/2,
33	 export/3,
34	 export_content/2,
35	 export_element/2,
36	 export_element/3,
37	 export_simple/2,
38	 export_simple/3,
39	 export_simple_element/2,
40	 export_simple_content/2,
41	 callbacks/1]).
42
43-include("xmerl.hrl").
44-include("xmerl_internal.hrl").
45
46
47%% @spec export(Content, Callback) -> ExportedFormat
48%% @equiv export(Data, Callback, [])
49
50export(Content, Callback) ->
51    export(Content, Callback, []).
52
53%% @spec export(Content, Callback, RootAttributes) -> ExportedFormat
54%%	Content = [Element]
55%%	Callback = atom()
56%%      RootAttributes = [XmlAttributes]
57%% @doc Exports normal, well-formed XML content, using the specified
58%% callback-module.
59%% <p><code>Element</code> is any of:</p>
60%% <ul>
61%% 	<li><code>#xmlText{}</code></li>
62%%	<li><code>#xmlElement{}</code></li>
63%%	<li><code>#xmlPI{}</code></li>
64%%	<li><code>#xmlComment{}</code></li>
65%%	<li><code>#xmlDecl{}</code></li>
66%% </ul>
67%% <p>(See <tt>xmerl.hrl</tt> for the record definitions.)
68%% Text in <code>#xmlText{}</code> elements can be deep lists of
69%% characters and/or binaries.</p>
70%%
71%% <p><code>RootAttributes</code> is a list of
72%% <code>#xmlAttribute{}</code> attributes for the <code>#root#</code>
73%% element, which implicitly becomes the parent of the given
74%% <code>Content</code>. The tag-handler function for
75%% <code>#root#</code> is thus called with the complete exported data of
76%% <code>Content</code>. Root attributes can be used to specify
77%% e.g. encoding or other metadata of an XML or HTML document.</p>
78%%
79%% <p>The <code>Callback</code> module should contain hook functions for
80%% all tags present in the data structure. A hook function must have the
81%% following format:</p>
82%% <pre>    Tag(Data, Attributes, Parents, E)</pre>
83%% <p>where <code>E</code> is the corresponding <code>#xmlElement{}</code>,
84%% <code>Data</code> is the already-exported contents of <code>E</code>
85%% and <code>Attributes</code> is the list of
86%% <code>#xmlAttribute{}</code> records of <code>E</code>. Finally,
87%% <code>Parents</code> is the list of parent nodes of <code>E</code>,
88%% on the form <code>[{ParentTag::atom(),
89%% ParentPosition::integer()}]</code>.</p>
90%%
91%% <p>The hook function should return either the data to be exported, or
92%% a tuple <code>{'#xml-alias#', NewTag::atom()}</code>, or a tuple
93%% <code>{'#xml-redefine#', Content}</code>, where <code>Content</code>
94%% is a content list (which can be on simple-form; see
95%% <code>export_simple/2</code> for details).</p>
96%%
97%% <p>A callback module can inherit definitions from other callback
98%% modules, through the required function <code>'#xml-interitance#() ->
99%% [ModuleName::atom()]</code>.</p>
100%%
101%% @see export/2
102%% @see export_simple/3
103
104export(Content, Callback, RootAttributes) when is_atom(Callback) ->
105    export1(Content, callbacks(Callback), RootAttributes);
106export(Content, Callbacks, RootAttrs) when is_list(Callbacks) ->
107    export1(Content, Callbacks, RootAttrs).
108
109%% @spec export_simple(Content, Callback) -> ExportedFormat
110%% @equiv export_simple(Content, Callback, [])
111
112export_simple(Content, Callback) ->
113    export_simple(Content, Callback, []).
114
115%% @spec export_simple(Content, Callback, RootAttributes) -> ExportedFormat
116%%	Content = [Element]
117%%	Callback = atom()
118%%      RootAttributes = [XmlAttributes]
119%% @doc Exports "simple-form" XML content, using the specified
120%% callback-module.
121%% <p><code>Element</code> is any of:</p>
122%% <ul>
123%%	<li><code>{Tag, Attributes, Content}</code></li>
124%%	<li><code>{Tag, Content}</code></li>
125%%	<li><code>Tag</code></li>
126%%	<li><code>IOString</code></li>
127%% 	<li><code>#xmlText{}</code></li>
128%%	<li><code>#xmlElement{}</code></li>
129%%	<li><code>#xmlPI{}</code></li>
130%%	<li><code>#xmlComment{}</code></li>
131%%	<li><code>#xmlDecl{}</code></li>
132%% </ul>
133%% <p>where</p>
134%% <ul>
135%%	<li><code>Tag = atom()</code></li>
136%%	<li><code>Attributes = [{Name, Value}]</code></li>
137%%	<li><code>Name = atom()</code></li>
138%%	<li><code>Value = IOString | atom() | integer()</code></li>
139%% </ul>
140%% <p>Normal-form XML elements can thus be included in the simple-form
141%% representation. Note that content lists must be flat. An
142%% <code>IOString</code> is a (possibly deep) list of characters and/or
143%% binaries.</p>
144%%
145%% <p><code>RootAttributes</code> is a list of:</p>
146%% <ul>
147%%	<li><code>XmlAttributes = #xmlAttribute{}</code></li>
148%%</ul>
149%%
150%% <p>See <code>export/3</code> for details on the callback module and
151%% the root attributes. The XML-data is always converted to normal form
152%% before being passed to the callback module.</p>
153%%
154%% @see export/3
155%% @see export_simple/2
156
157export_simple(Content, Callback, RootAttrs) when is_atom(Callback) ->
158    export_simple1(Content, callbacks(Callback), RootAttrs);
159export_simple(Content, Callbacks, RootAttrs) when is_list(Callbacks) ->
160    export_simple1(Content, Callbacks, RootAttrs).
161
162export_simple1(Content, Callback, RootAttrs) ->
163    export1(xmerl_lib:expand_content(Content), Callback, RootAttrs).
164
165%% This exports proper XML content in root context.
166
167export1(Content, Callbacks, RootAttrs) when is_list(Content) ->
168    Result = export_content(Content, Callbacks),
169    Attrs = xmerl_lib:expand_attributes(RootAttrs, 1, [{'#root#',1}]),
170    Root = #xmlElement{name = '#root#',
171		       pos = 1,
172		       parents = [],
173		       attributes = Attrs},
174    Args = [Result, Root#xmlElement.attributes, [], Root],
175    tagdef('#root#',1,[],Args,Callbacks).
176
177%% @doc Exports simple XML content directly, without further context.
178
179export_simple_content(Content, Callback) when is_atom(Callback) ->
180    export_content(xmerl_lib:expand_content(Content),
181		   callbacks(Callback));
182export_simple_content(Content, Callbacks) when is_list(Callbacks) ->
183    export_content(xmerl_lib:expand_content(Content), Callbacks).
184
185
186%% @spec export_content(Content, Callbacks) -> term()
187%%	Content = [Element]
188%%	Callback = [atom()]
189%% @doc Exports normal XML content directly, without further context.
190export_content([#xmlText{value = Text} | Es], Callbacks) ->
191    [apply_text_cb(Callbacks, Text) | export_content(Es, Callbacks)];
192export_content([#xmlPI{} | Es], Callbacks) ->
193    export_content(Es, Callbacks);
194export_content([#xmlComment{} | Es], Callbacks) ->
195    export_content(Es, Callbacks);
196export_content([#xmlDecl{} | Es], Callbacks) ->
197    export_content(Es, Callbacks);
198export_content([E | Es], Callbacks) ->
199    [export_element(E, Callbacks) | export_content(Es, Callbacks)];
200export_content([], _Callbacks) ->
201    [].
202
203%% @doc Exports a simple XML element directly, without further context.
204
205export_simple_element(Content, Callback) when is_atom(Callback) ->
206    export_element(xmerl_lib:expand_element(Content),
207		   callbacks(Callback));
208export_simple_element(Content, Callbacks) when is_list(Callbacks) ->
209    export_element(xmerl_lib:expand_element(Content), Callbacks).
210
211%% @doc Exports a normal XML element directly, without further context.
212
213%% This is the usual DOM style parsing.
214
215export_element(E, CB) when is_atom(CB) ->
216    export_element(E, callbacks(CB));
217export_element(#xmlText{value = Text}, CBs) ->
218    apply_text_cb(CBs, Text);
219export_element(E = #xmlElement{name = Tag,
220			       pos = Pos,
221			       attributes = Attributes,
222			       parents = Parents,
223			       content = Content}, CBs) ->
224    Data = export_content(Content, CBs),
225    Args = [Data, Attributes, Parents, E],
226    tagdef(Tag,Pos,Parents,Args,CBs);
227export_element(#xmlPI{}, _CBs) ->
228    [];
229export_element(#xmlComment{}, _CBs) ->
230    [];
231export_element(#xmlDecl{}, _CBs) ->
232    [].
233
234
235%% @spec export_element(E,CallbackModule,CallbackState) -> ExportedFormat
236%% @doc For on-the-fly exporting during parsing (SAX style) of the XML
237%% document.
238export_element(E, CallbackModule, CallbackState) when is_atom(CallbackModule) ->
239    export_element(E, callbacks(CallbackModule), CallbackState);
240export_element(#xmlText{value = Text},CallbackModule,_CallbackState) ->
241%%    apply_cb(CallbackModule, '#text#', '#text#', [Text,CallbackState]);
242    apply_text_cb(CallbackModule,Text);
243export_element(E=#xmlElement{name = Tag,
244			   pos = Pos,
245			   parents = Parents,
246			   attributes = Attributes,
247			   content = Content},Callbacks,CBstate) ->
248    Args = [Content, Attributes,CBstate,E],
249    tagdef(Tag,Pos,Parents,Args,Callbacks);
250export_element(#xmlPI{}, _CallbackModule, CallbackState) ->
251    CallbackState;
252export_element(#xmlComment{},_CallbackModule, CallbackState) ->
253    CallbackState;
254export_element(#xmlDecl{},_CallbackModule, CallbackState) ->
255    CallbackState.
256
257%% A thing returned with #xml-redefine is assumed to be a content list
258%% The data may be on "simple" format.
259
260tagdef(Tag,Pos,Parents,Args,CBs) ->
261    case apply_tag_cb(CBs, Tag, Args) of
262	{'#xml-alias#', NewTag} ->
263	    tagdef(NewTag,Pos,Parents,Args,CBs);
264	{'#xml-redefine#', Data} ->
265	    export_content(xmerl_lib:expand_content(Data, Pos, Parents),
266			   CBs);
267	Other ->
268	    Other
269    end.
270
271%% @spec callbacks(Module) -> Result
272%%	Module = atom()
273%%	Result = [atom()]
274%% @doc Find the list of inherited callback modules for a given module.
275
276callbacks(Module) ->
277    Result = check_inheritance(Module, []),
278%%%     ?dbg("callbacks = ~p~n", [lists:reverse(Result)]),
279    lists:reverse(Result).
280
281callbacks([M|Mods], Visited) ->
282    case lists:member(M, Visited) of
283	false ->
284	    NewVisited = check_inheritance(M, Visited),
285	    callbacks(Mods, NewVisited);
286	true ->
287	    exit({cyclic_inheritance, {M, hd(Visited)}})
288    end;
289callbacks([], Visited) ->
290    Visited.
291
292check_inheritance(M, Visited) ->
293%%%     ?dbg("calling ~p:'#xml-inheritance#'()~n", [M]),
294    case M:'#xml-inheritance#'() of
295	[] ->
296	    [M|Visited];
297	Mods ->
298	    callbacks(Mods, [M|Visited])
299    end.
300
301apply_text_cb(Ms, Text) ->
302    apply_cb(Ms, '#text#', '#text#', [Text]).
303
304apply_tag_cb(Ms, F, Args) ->
305    apply_cb(Ms, F, '#element#', Args).
306
307apply_cb(Ms, F, Df, Args) ->
308    apply_cb(Ms, F, Df, Args, length(Args)).
309
310apply_cb(Ms, F, Df, Args, A) ->
311    apply_cb(Ms, F, Df, Args, A, Ms).
312
313apply_cb([M|Ms], F, Df, Args, A, Ms0) ->
314    case erlang:function_exported(M, F, A) of
315        true -> apply(M, F, Args);
316        false -> apply_cb(Ms, F, Df, Args, A, Ms0)
317    end;
318apply_cb([], Df, Df, Args, _A, _Ms0) ->
319    exit({unknown_tag, {Df, Args}});
320apply_cb([], F, Df, Args, A, Ms0) ->
321    apply_cb(Ms0, Df, Df, [F|Args], A+1).
322