%%-*-erlang-*-
%%--------------------------------------------------------------------
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2009. 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%
%%----------------------------------------------------------------------
%% File : xmerl_sax_simple_dom.erl
%% Description :
%%
%% Created : 17 Apr 2009
%%----------------------------------------------------------------------
-module(xmerl_sax_simple_dom).
%%----------------------------------------------------------------------
%% Include files
%%----------------------------------------------------------------------
-include("xmerl_sax_old_dom.hrl").
-include("xmerl_internal.hrl").
%%----------------------------------------------------------------------
%% External exports
%%----------------------------------------------------------------------
-export([
initial_state/0,
get_dom/1,
event/3
]).
%%----------------------------------------------------------------------
%% Internal exports
%%----------------------------------------------------------------------
-export([
]).
%%======================================================================
%% Macros
%%======================================================================
%%----------------------------------------------------------------------
%% Error handling
%%----------------------------------------------------------------------
-define(error(Reason),
throw({sax_simple_dom_error, Reason})).
%%----------------------------------------------------------------------
%%======================================================================
%% Records
%%======================================================================
%%----------------------------------------------------------------------
%% State record for the validator
%%----------------------------------------------------------------------
-record(xmerl_sax_simple_dom_state, {
tags=[], %% Tag stack
cno=[], %% Current node number
namespaces = [], %% NameSpace stack
dom=[] %% DOM structure
}).
%%======================================================================
%% External functions
%%======================================================================
%%----------------------------------------------------------------------
%% Function: initial_state() -> Result
%% Parameters:
%% Result:
%% Description:
%%----------------------------------------------------------------------
initial_state() ->
#xmerl_sax_simple_dom_state{}.
%%----------------------------------------------------------------------
%% Function: get_dom(State) -> Result
%% Parameters:
%% Result:
%% Description:
%%----------------------------------------------------------------------
get_dom(#xmerl_sax_simple_dom_state{dom=Dom}) ->
Dom.
%%----------------------------------------------------------------------
%% Function: event(Event, LineNo, State) -> Result
%% Parameters:
%% Result:
%% Description:
%%----------------------------------------------------------------------
event(Event, _LineNo, State) ->
build_dom(Event, State).
%%======================================================================
%% Internal functions
%%======================================================================
%%----------------------------------------------------------------------
%% Function : build_dom(Event, State) -> Result
%% Parameters: Event = term()
%% State = #xmerl_sax_simple_dom_state{}
%% Result : #xmerl_sax_simple_dom_state{} |
%% Description:
%%----------------------------------------------------------------------
%% Document
%%----------------------------------------------------------------------
build_dom(startDocument, State) ->
State#xmerl_sax_simple_dom_state{dom=[startDocument]};
build_dom(endDocument,
#xmerl_sax_simple_dom_state{dom=[{Tag, Attributes, Content} |D]} = State) ->
case D of
[startDocument] ->
State#xmerl_sax_simple_dom_state{dom=[{Tag, Attributes,
lists:reverse(Content)}]};
[#xmlDecl{} = Decl, startDocument] ->
State#xmerl_sax_simple_dom_state{dom=[Decl, {Tag, Attributes,
lists:reverse(Content)}]};
_ ->
?dbg("~p\n", [D]),
?error("we're not at end the document when endDocument event is encountered.")
end;
%% Element
%%----------------------------------------------------------------------
build_dom({startElement, _Uri, LocalName, _QName, Attributes},
#xmerl_sax_simple_dom_state{tags=T, cno=CN, dom=D} = State) ->
A = parse_attributes(LocalName, Attributes),
{Num, NewCN} =
case CN of
[] ->
{1, [1]};
[ N |CNs] ->
{N, [1, N+1 |CNs]}
end,
State#xmerl_sax_simple_dom_state{tags=[{list_to_atom(LocalName), Num} |T],
cno=NewCN,
dom=[{list_to_atom(LocalName),
lists:reverse(A),
[]
} | D]};
build_dom({endElement, _Uri, LocalName, _QName},
#xmerl_sax_simple_dom_state{tags=[_ |T],
cno=[_ |CN],
dom=[{CName, CAttributes, CContent},
{PName, PAttributes, PContent} | D]} = State) ->
case list_to_atom(LocalName) of
CName ->
State#xmerl_sax_simple_dom_state{tags=T,
cno=CN,
dom=[{PName, PAttributes,
[{CName, CAttributes,
lists:reverse(CContent)}
|PContent]
} | D]};
_ ->
?error("Got end of element: " ++ LocalName ++ " but expected: " ++
CName)
end;
%% Text
%%----------------------------------------------------------------------
build_dom({characters, String},
#xmerl_sax_simple_dom_state{tags=_T,
cno=[Num |CN],
dom=[{Name, Attributes, Content}| D]} = State) ->
State#xmerl_sax_simple_dom_state{cno=[Num+1 |CN],
dom=[{Name, Attributes, [String|Content]} | D]};
build_dom({ignorableWhitespace, String},
#xmerl_sax_simple_dom_state{tags=T,
cno=[Num |CN],
dom=[{Name, Attributes, Content}| D]} = State) ->
State#xmerl_sax_simple_dom_state{cno=[Num+1 |CN],
dom=[{Name, Attributes, [#xmlText{value=String, parents=T, pos=Num, type=text}|Content]} | D]};
%% Comments
%%----------------------------------------------------------------------
build_dom({comment, String},
#xmerl_sax_simple_dom_state{tags=T,
cno=[Num |CN],
dom=[#xmlElement{content=C} = Current| D]} = State) ->
State#xmerl_sax_simple_dom_state{cno=[Num+1 |CN],
dom=[Current#xmlElement{content=[#xmlComment{parents=T, pos=Num, value=String}|C]} | D]};
%% NameSpaces
%%----------------------------------------------------------------------
build_dom({startPrefixMapping, [], _Uri}, State) ->
State;
build_dom({startPrefixMapping, Prefix, Uri},
#xmerl_sax_simple_dom_state{namespaces=NS} = State) ->
State#xmerl_sax_simple_dom_state{namespaces=[{Prefix, list_to_atom(Uri)} |NS]};
build_dom({endPrefixMapping, Prefix},
#xmerl_sax_simple_dom_state{namespaces=[{Prefix, _} |NS]} = State) ->
State#xmerl_sax_simple_dom_state{namespaces=NS};
%% Processing instructions
%%----------------------------------------------------------------------
build_dom({processingInstruction,"xml", PiData},
#xmerl_sax_simple_dom_state{dom=D} = State) ->
{Vsn, PiData1} = find_and_remove_attribute("version", PiData, []),
{Enc, PiData2} = find_and_remove_attribute("encoding", PiData1, []),
{Standalone, PiData3} = find_and_remove_attribute("standalone", PiData2, yes),
State#xmerl_sax_simple_dom_state{dom=[#xmlDecl{vsn=Vsn, encoding=Enc, standalone=Standalone, attributes=PiData3}| D]};
build_dom({processingInstruction, PiTarget, PiData},
#xmerl_sax_simple_dom_state{cno=[Num |CN],
dom=[#xmlElement{content=C} = Current| D]} = State) ->
State#xmerl_sax_simple_dom_state{cno=[Num+1 |CN],
dom=[Current#xmlElement{content=[#xmlPI{name=PiTarget,pos=Num, value=PiData}
|C]} | D]};
%% Default
%%----------------------------------------------------------------------
build_dom(_E, State) ->
State.
%%----------------------------------------------------------------------
%% Function : parse_attributes(ElName, Attributes) -> Result
%% Parameters:
%% Result :
%% Description:
%%----------------------------------------------------------------------
parse_attributes(ElName, Attributes) ->
parse_attributes(ElName, Attributes, 1, []).
parse_attributes(_, [], _, Acc) ->
Acc;
parse_attributes(ElName, [{_Uri, _Prefix, LocalName, AttrValue} |As], N, Acc) ->
parse_attributes(ElName, As, N+1, [{list_to_atom(LocalName), AttrValue} |Acc]).
%%----------------------------------------------------------------------
%% Function : find_and_remove_attribute(Key, Data, Default) -> Result
%% Parameters:
%% Result :
%% Description:
%%----------------------------------------------------------------------
find_and_remove_attribute(Key, Data, Default) ->
case lists:keysearch(Key, 1, Data) of
{value, {Key, Value}} ->
Data2 = lists:keydelete(Key, 1, Data),
{Value, Data2};
false ->
{Default, Data}
end.