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