aboutsummaryrefslogtreecommitdiffstats
path: root/lib/xmerl/src/xmerl.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/xmerl/src/xmerl.erl')
-rw-r--r--lib/xmerl/src/xmerl.erl320
1 files changed, 320 insertions, 0 deletions
diff --git a/lib/xmerl/src/xmerl.erl b/lib/xmerl/src/xmerl.erl
new file mode 100644
index 0000000000..cf78f7bdf7
--- /dev/null
+++ b/lib/xmerl/src/xmerl.erl
@@ -0,0 +1,320 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% Description : Functions to export simple and complete XML forms
+%%
+
+%% @doc Functions for exporting XML data to an external format.
+%%
+
+-module(xmerl).
+
+%-compile(export_all).
+
+-export([export/2,
+ export/3,
+ export_content/2,
+ export_element/2,
+ export_element/3,
+ export_simple/2,
+ export_simple/3,
+ export_simple_element/2,
+ export_simple_content/2,
+ callbacks/1]).
+
+-include("xmerl.hrl").
+
+
+%% @spec export(Content, Callback) -> ExportedFormat
+%% @equiv export(Data, Callback, [])
+
+export(Content, Callback) ->
+ export(Content, Callback, []).
+
+%% @spec export(Content, Callback, RootAttributes) -> ExportedFormat
+%% Content = [Element]
+%% Callback = atom()
+%% RootAttributes = [XmlAttributes]
+%% @doc Exports normal, well-formed XML content, using the specified
+%% callback-module.
+%% <p><code>Element</code> is any of:</p>
+%% <ul>
+%% <li><code>#xmlText{}</code></li>
+%% <li><code>#xmlElement{}</code></li>
+%% <li><code>#xmlPI{}</code></li>
+%% <li><code>#xmlComment{}</code></li>
+%% <li><code>#xmlDecl{}</code></li>
+%% </ul>
+%% <p>(See <tt>xmerl.hrl</tt> for the record definitions.)
+%% Text in <code>#xmlText{}</code> elements can be deep lists of
+%% characters and/or binaries.</p>
+%%
+%% <p><code>RootAttributes</code> is a list of
+%% <code>#xmlAttribute{}</code> attributes for the <code>#root#</code>
+%% element, which implicitly becomes the parent of the given
+%% <code>Content</code>. The tag-handler function for
+%% <code>#root#</code> is thus called with the complete exported data of
+%% <code>Content</code>. Root attributes can be used to specify
+%% e.g. encoding or other metadata of an XML or HTML document.</p>
+%%
+%% <p>The <code>Callback</code> module should contain hook functions for
+%% all tags present in the data structure. A hook function must have the
+%% following format:</p>
+%% <pre> Tag(Data, Attributes, Parents, E)</pre>
+%% <p>where <code>E</code> is the corresponding <code>#xmlElement{}</code>,
+%% <code>Data</code> is the already-exported contents of <code>E</code>
+%% and <code>Attributes</code> is the list of
+%% <code>#xmlAttribute{}</code> records of <code>E</code>. Finally,
+%% <code>Parents</code> is the list of parent nodes of <code>E</code>,
+%% on the form <code>[{ParentTag::atom(),
+%% ParentPosition::integer()}]</code>.</p>
+%%
+%% <p>The hook function should return either the data to be exported, or
+%% a tuple <code>{'#xml-alias#', NewTag::atom()}</code>, or a tuple
+%% <code>{'#xml-redefine#', Content}</code>, where <code>Content</code>
+%% is a content list (which can be on simple-form; see
+%% <code>export_simple/2</code> for details).</p>
+%%
+%% <p>A callback module can inherit definitions from other callback
+%% modules, through the required function <code>'#xml-interitance#() ->
+%% [ModuleName::atom()]</code>.</p>
+%%
+%% @see export/2
+%% @see export_simple/3
+
+export(Content, Callback, RootAttributes) when is_atom(Callback) ->
+ export1(Content, callbacks(Callback), RootAttributes);
+export(Content, Callbacks, RootAttrs) when is_list(Callbacks) ->
+ export1(Content, Callbacks, RootAttrs).
+
+%% @spec export_simple(Content, Callback) -> ExportedFormat
+%% @equiv export_simple(Content, Callback, [])
+
+export_simple(Content, Callback) ->
+ export_simple(Content, Callback, []).
+
+%% @spec export_simple(Content, Callback, RootAttributes) -> ExportedFormat
+%% Content = [Element]
+%% Callback = atom()
+%% RootAttributes = [XmlAttributes]
+%% @doc Exports "simple-form" XML content, using the specified
+%% callback-module.
+%% <p><code>Element</code> is any of:</p>
+%% <ul>
+%% <li><code>{Tag, Attributes, Content}</code></li>
+%% <li><code>{Tag, Content}</code></li>
+%% <li><code>Tag</code></li>
+%% <li><code>IOString</code></li>
+%% <li><code>#xmlText{}</code></li>
+%% <li><code>#xmlElement{}</code></li>
+%% <li><code>#xmlPI{}</code></li>
+%% <li><code>#xmlComment{}</code></li>
+%% <li><code>#xmlDecl{}</code></li>
+%% </ul>
+%% <p>where</p>
+%% <ul>
+%% <li><code>Tag = atom()</code></li>
+%% <li><code>Attributes = [{Name, Value}]</code></li>
+%% <li><code>Name = atom()</code></li>
+%% <li><code>Value = IOString | atom() | integer()</code></li>
+%% </ul>
+%% <p>Normal-form XML elements can thus be included in the simple-form
+%% representation. Note that content lists must be flat. An
+%% <code>IOString</code> is a (possibly deep) list of characters and/or
+%% binaries.</p>
+%%
+%% <p><code>RootAttributes</code> is a list of:</p>
+%% <ul>
+%% <li><code>XmlAttributes = #xmlAttribute{}</code></li>
+%%</ul>
+%%
+%% <p>See <code>export/3</code> for details on the callback module and
+%% the root attributes. The XML-data is always converted to normal form
+%% before being passed to the callback module.</p>
+%%
+%% @see export/3
+%% @see export_simple/2
+
+export_simple(Content, Callback, RootAttrs) when is_atom(Callback) ->
+ export_simple1(Content, callbacks(Callback), RootAttrs);
+export_simple(Content, Callbacks, RootAttrs) when is_list(Callbacks) ->
+ export_simple1(Content, Callbacks, RootAttrs).
+
+export_simple1(Content, Callback, RootAttrs) ->
+ export1(xmerl_lib:expand_content(Content), Callback, RootAttrs).
+
+%% This exports proper XML content in root context.
+
+export1(Content, Callbacks, RootAttrs) when is_list(Content) ->
+ Result = export_content(Content, Callbacks),
+ Attrs = xmerl_lib:expand_attributes(RootAttrs, 1, [{'#root#',1}]),
+ Root = #xmlElement{name = '#root#',
+ pos = 1,
+ parents = [],
+ attributes = Attrs},
+ Args = [Result, Root#xmlElement.attributes, [], Root],
+ tagdef('#root#',1,[],Args,Callbacks).
+
+%% @doc Exports simple XML content directly, without further context.
+
+export_simple_content(Content, Callback) when is_atom(Callback) ->
+ export_content(xmerl_lib:expand_content(Content),
+ callbacks(Callback));
+export_simple_content(Content, Callbacks) when is_list(Callbacks) ->
+ export_content(xmerl_lib:expand_content(Content), Callbacks).
+
+
+%% @spec export_content(Content, Callbacks) -> term()
+%% Content = [Element]
+%% Callback = [atom()]
+%% @doc Exports normal XML content directly, without further context.
+export_content([#xmlText{value = Text} | Es], Callbacks) ->
+ [apply_text_cb(Callbacks, Text) | export_content(Es, Callbacks)];
+export_content([#xmlPI{} | Es], Callbacks) ->
+ export_content(Es, Callbacks);
+export_content([#xmlComment{} | Es], Callbacks) ->
+ export_content(Es, Callbacks);
+export_content([#xmlDecl{} | Es], Callbacks) ->
+ export_content(Es, Callbacks);
+export_content([E | Es], Callbacks) ->
+ [export_element(E, Callbacks) | export_content(Es, Callbacks)];
+export_content([], _Callbacks) ->
+ [].
+
+%% @doc Exports a simple XML element directly, without further context.
+
+export_simple_element(Content, Callback) when is_atom(Callback) ->
+ export_element(xmerl_lib:expand_element(Content),
+ callbacks(Callback));
+export_simple_element(Content, Callbacks) when is_list(Callbacks) ->
+ export_element(xmerl_lib:expand_element(Content), Callbacks).
+
+%% @doc Exports a normal XML element directly, without further context.
+
+%% This is the usual DOM style parsing.
+
+export_element(E, CB) when is_atom(CB) ->
+ export_element(E, callbacks(CB));
+export_element(#xmlText{value = Text}, CBs) ->
+ apply_text_cb(CBs, Text);
+export_element(E = #xmlElement{name = Tag,
+ pos = Pos,
+ attributes = Attributes,
+ parents = Parents,
+ content = Content}, CBs) ->
+ Data = export_content(Content, CBs),
+ Args = [Data, Attributes, Parents, E],
+ tagdef(Tag,Pos,Parents,Args,CBs);
+export_element(#xmlPI{}, _CBs) ->
+ [];
+export_element(#xmlComment{}, _CBs) ->
+ [];
+export_element(#xmlDecl{}, _CBs) ->
+ [].
+
+
+%% @spec export_element(E,CallbackModule,CallbackState) -> ExportedFormat
+%% @doc For on-the-fly exporting during parsing (SAX style) of the XML
+%% document.
+export_element(E, CallbackModule, CallbackState) when is_atom(CallbackModule) ->
+ export_element(E, callbacks(CallbackModule), CallbackState);
+export_element(#xmlText{value = Text},CallbackModule,_CallbackState) ->
+%% apply_cb(CallbackModule, '#text#', '#text#', [Text,CallbackState]);
+ apply_text_cb(CallbackModule,Text);
+export_element(E=#xmlElement{name = Tag,
+ pos = Pos,
+ parents = Parents,
+ attributes = Attributes,
+ content = Content},Callbacks,CBstate) ->
+ Args = [Content, Attributes,CBstate,E],
+ tagdef(Tag,Pos,Parents,Args,Callbacks);
+export_element(#xmlPI{}, _CallbackModule, CallbackState) ->
+ CallbackState;
+export_element(#xmlComment{},_CallbackModule, CallbackState) ->
+ CallbackState;
+export_element(#xmlDecl{},_CallbackModule, CallbackState) ->
+ CallbackState.
+
+%% A thing returned with #xml-redefine is assumed to be a content list
+%% The data may be on "simple" format.
+
+tagdef(Tag,Pos,Parents,Args,CBs) ->
+ case apply_tag_cb(CBs, Tag, Args) of
+ {'#xml-alias#', NewTag} ->
+ tagdef(NewTag,Pos,Parents,Args,CBs);
+ {'#xml-redefine#', Data} ->
+ export_content(xmerl_lib:expand_content(Data, Pos, Parents),
+ CBs);
+ Other ->
+ Other
+ end.
+
+%% @spec callbacks(Module) -> Result
+%% Module = atom()
+%% Result = [atom()]
+%% @doc Find the list of inherited callback modules for a given module.
+
+callbacks(Module) ->
+ Result = check_inheritance(Module, []),
+%%% io:format("callbacks = ~p~n", [lists:reverse(Result)]),
+ lists:reverse(Result).
+
+callbacks([M|Mods], Visited) ->
+ case lists:member(M, Visited) of
+ false ->
+ NewVisited = check_inheritance(M, Visited),
+ callbacks(Mods, NewVisited);
+ true ->
+ exit({cyclic_inheritance, {M, hd(Visited)}})
+ end;
+callbacks([], Visited) ->
+ Visited.
+
+check_inheritance(M, Visited) ->
+%%% io:format("calling ~p:'#xml-inheritance#'()~n", [M]),
+ case M:'#xml-inheritance#'() of
+ [] ->
+ [M|Visited];
+ Mods ->
+ callbacks(Mods, [M|Visited])
+ end.
+
+apply_text_cb(Ms, Text) ->
+ apply_cb(Ms, '#text#', '#text#', [Text]).
+
+apply_tag_cb(Ms, F, Args) ->
+ apply_cb(Ms, F, '#element#', Args).
+
+apply_cb(Ms, F, Df, Args) ->
+ apply_cb(Ms, F, Df, Args, Ms).
+
+apply_cb([M|Ms], F, Df, Args, Ms0) ->
+ case catch apply(M, F, Args) of
+ {'EXIT', {undef,[{M,F,_}|_]}} ->
+ apply_cb(Ms, F, Df, Args, Ms0);
+ {'EXIT', Reason} ->
+ exit(Reason);
+ Res ->
+ Res
+ end;
+apply_cb([], Df, Df, Args, _Ms0) ->
+ exit({unknown_tag, {Df, Args}});
+apply_cb([], F, Df, Args, Ms0) ->
+ apply_cb(Ms0, Df, Df, [F|Args]).