%%-*-erlang-*- %%-------------------------------------------------------------------- %% %CopyrightBegin% %% %% Copyright Ericsson AB 2009-2017. 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_old_dom.erl %% Description : %% %% Created : 02 Oct 2008 %%---------------------------------------------------------------------- -module(xmerl_sax_old_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({xmerl_sax_old_dom_error, Reason})). %%====================================================================== %% Records %%====================================================================== %%---------------------------------------------------------------------- %% State record for the validator %%---------------------------------------------------------------------- -record(xmerl_sax_old_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_old_dom_state{}. %%---------------------------------------------------------------------- %% Function: get_dom(State) -> Result %% Parameters: %% Result: %% Description: %%---------------------------------------------------------------------- get_dom(#xmerl_sax_old_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_old_dom_state{} %% Result : #xmerl_sax_old_dom_state{} | %% Description: %%---------------------------------------------------------------------- %% Document %%---------------------------------------------------------------------- build_dom(startDocument, State) -> State#xmerl_sax_old_dom_state{dom=[startDocument]}; build_dom(endDocument, #xmerl_sax_old_dom_state{dom=[#xmlElement{content=C} = Current |D]} = State) -> case D of [startDocument] -> State#xmerl_sax_old_dom_state{dom=[Current#xmlElement{ content=lists:reverse(C) }]}; [#xmlDecl{} = Decl, startDocument] -> State#xmerl_sax_old_dom_state{dom=[Decl, Current#xmlElement{ content=lists:reverse(C) }]}; _ -> %% endDocument is also sent by the parser when a fault occur to tell %% the event receiver that no more input will be sent State end; %% Element %%---------------------------------------------------------------------- build_dom({startElement, Uri, LocalName, QName, Attributes}, #xmerl_sax_old_dom_state{tags=T, cno=CN, namespaces=NS, dom=D} = State) -> A = parse_attributes(LocalName, Attributes), {Num, NewCN} = case CN of [] -> {1, [1]}; [ N |CNs] -> {N, [1, N+1 |CNs]} end, NsInfo = case QName of {[], _} -> []; QN -> QN end, NameAsAtom = convert_qname_to_atom(QName), State#xmerl_sax_old_dom_state{tags=[{NameAsAtom, Num} |T], cno=NewCN, dom=[#xmlElement{name=NameAsAtom, expanded_name=NameAsAtom, nsinfo=NsInfo, namespace=#xmlNamespace{default=list_to_atom(Uri), nodes=NS}, pos=Num, parents=T, attributes=lists:reverse(A), xmlbase="." } | D]}; build_dom({endElement, _Uri, LocalName, QName}, #xmerl_sax_old_dom_state{tags=[_ |T], cno=[_ |CN], dom=[#xmlElement{name=CName, content=C} = Current, #xmlElement{content=PC} = Parent | D]} = State) -> case convert_qname_to_atom(QName) of CName -> State#xmerl_sax_old_dom_state{tags=T, cno=CN, dom=[Parent#xmlElement{ content=[Current#xmlElement{ content=lists:reverse(C) } |PC] } | D]}; _ -> ?error("Got end of element: " ++ LocalName ++ " but expected: " ++ Current#xmlElement.name) end; %% Text %%---------------------------------------------------------------------- build_dom({characters, String}, #xmerl_sax_old_dom_state{tags=T, cno=[Num |CN], dom=[#xmlElement{content=C} = Current| D]} = State) -> State#xmerl_sax_old_dom_state{cno=[Num+1 |CN], dom=[Current#xmlElement{content=[#xmlText{value=String, parents=T, pos=Num, type=text} |C]} | D]}; build_dom({ignorableWhitespace, String}, #xmerl_sax_old_dom_state{tags=T, cno=[Num |CN], dom=[#xmlElement{content=C} = Current| D]} = State) -> State#xmerl_sax_old_dom_state{cno=[Num+1 |CN], dom=[Current#xmlElement{content=[#xmlText{value=String, parents=T, pos=Num, type=text} |C]} | D]}; %% Comments %%---------------------------------------------------------------------- build_dom({comment, String}, #xmerl_sax_old_dom_state{tags=T, cno=[Num |CN], dom=[#xmlElement{content=C} = Current| D]} = State) -> State#xmerl_sax_old_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_old_dom_state{namespaces=NS} = State) -> State#xmerl_sax_old_dom_state{namespaces=[{Prefix, list_to_atom(Uri)} |NS]}; build_dom({endPrefixMapping, Prefix}, #xmerl_sax_old_dom_state{namespaces=[{Prefix, _} |NS]} = State) -> State#xmerl_sax_old_dom_state{namespaces=NS}; %% Processing instructions %%---------------------------------------------------------------------- build_dom({processingInstruction,"xml", PiData}, #xmerl_sax_old_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_old_dom_state{dom=[#xmlDecl{vsn=Vsn, encoding=Enc, standalone=Standalone, attributes=PiData3}| D]}; build_dom({processingInstruction, PiTarget, PiData}, #xmerl_sax_old_dom_state{cno=[Num |CN], dom=[#xmlElement{content=C} = Current| D]} = State) -> State#xmerl_sax_old_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) -> Name = convert_qname_to_atom({Prefix,LocalName}), NsInfo = case Prefix of [] -> []; P -> {P,LocalName} end, parse_attributes(ElName, As, N+1, [#xmlAttribute{name=Name, pos=N, nsinfo=NsInfo, value=AttrValue, normalized=false} |Acc]). %%---------------------------------------------------------------------- %% Function : convert_qname_to_atom(QName) -> Result %% Parameters: %% Result : %% Description: %%---------------------------------------------------------------------- convert_qname_to_atom({[], N}) -> list_to_atom(N); convert_qname_to_atom({P,N}) -> list_to_atom(P ++ ":" ++ N). %%---------------------------------------------------------------------- %% 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.