%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2003-2011. 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. %%

Element is any of:

%% %%

(See xmerl.hrl for the record definitions.) %% Text in #xmlText{} elements can be deep lists of %% characters and/or binaries.

%% %%

RootAttributes is a list of %% #xmlAttribute{} attributes for the #root# %% element, which implicitly becomes the parent of the given %% Content. The tag-handler function for %% #root# is thus called with the complete exported data of %% Content. Root attributes can be used to specify %% e.g. encoding or other metadata of an XML or HTML document.

%% %%

The Callback module should contain hook functions for %% all tags present in the data structure. A hook function must have the %% following format:

%%
    Tag(Data, Attributes, Parents, E)
%%

where E is the corresponding #xmlElement{}, %% Data is the already-exported contents of E %% and Attributes is the list of %% #xmlAttribute{} records of E. Finally, %% Parents is the list of parent nodes of E, %% on the form [{ParentTag::atom(), %% ParentPosition::integer()}].

%% %%

The hook function should return either the data to be exported, or %% a tuple {'#xml-alias#', NewTag::atom()}, or a tuple %% {'#xml-redefine#', Content}, where Content %% is a content list (which can be on simple-form; see %% export_simple/2 for details).

%% %%

A callback module can inherit definitions from other callback %% modules, through the required function '#xml-interitance#() -> %% [ModuleName::atom()].

%% %% @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. %%

Element is any of:

%% %%

where

%% %%

Normal-form XML elements can thus be included in the simple-form %% representation. Note that content lists must be flat. An %% IOString is a (possibly deep) list of characters and/or %% binaries.

%% %%

RootAttributes is a list of:

%% %% %%

See export/3 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.

%% %% @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, length(Args)). apply_cb(Ms, F, Df, Args, A) -> apply_cb(Ms, F, Df, Args, A, Ms). apply_cb([M|Ms], F, Df, Args, A, Ms0) -> case erlang:function_exported(M, F, A) of true -> apply(M, F, Args); false -> apply_cb(Ms, F, Df, Args, A, Ms0) end; apply_cb([], Df, Df, Args, A, _Ms0) -> exit({unknown_tag, {Df, Args}}); apply_cb([], F, Df, Args, A, Ms0) -> apply_cb(Ms0, Df, Df, [F|Args], A+1).