1%%-*-erlang-*-
2%%--------------------------------------------------------------------
3%% %CopyrightBegin%
4%%
5%% Copyright Ericsson AB 2009-2017. All Rights Reserved.
6%%
7%% Licensed under the Apache License, Version 2.0 (the "License");
8%% you may not use this file except in compliance with the License.
9%% You may obtain a copy of the License at
10%%
11%%     http://www.apache.org/licenses/LICENSE-2.0
12%%
13%% Unless required by applicable law or agreed to in writing, software
14%% distributed under the License is distributed on an "AS IS" BASIS,
15%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16%% See the License for the specific language governing permissions and
17%% limitations under the License.
18%%
19%% %CopyrightEnd%
20%%----------------------------------------------------------------------
21%% File    : xmerl_sax_old_dom.erl
22%% Description :
23%%
24%% Created : 02 Oct 2008
25%%----------------------------------------------------------------------
26-module(xmerl_sax_old_dom).
27
28%%----------------------------------------------------------------------
29%% Include files
30%%----------------------------------------------------------------------
31-include("xmerl_sax_old_dom.hrl").
32-include("xmerl_internal.hrl").
33
34%%----------------------------------------------------------------------
35%% External exports
36%%----------------------------------------------------------------------
37-export([
38	 initial_state/0,
39	 get_dom/1,
40	 event/3
41        ]).
42
43%%----------------------------------------------------------------------
44%% Internal exports
45%%----------------------------------------------------------------------
46-export([
47        ]).
48
49%%======================================================================
50%% Macros
51%%======================================================================
52%%----------------------------------------------------------------------
53%% Error handling
54%%----------------------------------------------------------------------
55-define(error(Reason),
56	throw({xmerl_sax_old_dom_error, Reason})).
57
58%%======================================================================
59%% Records
60%%======================================================================
61
62%%----------------------------------------------------------------------
63%% State record for the validator
64%%----------------------------------------------------------------------
65-record(xmerl_sax_old_dom_state, {
66	  tags=[],         %% Tag stack
67	  cno=[],          %% Current node number
68	  namespaces = [], %% NameSpace stack
69	  dom=[]           %% DOM structure
70	 }).
71
72%%======================================================================
73%% External functions
74%%======================================================================
75%%----------------------------------------------------------------------
76%% Function: initial_state() -> Result
77%% Parameters:
78%% Result:
79%% Description:
80%%----------------------------------------------------------------------
81initial_state() ->
82    #xmerl_sax_old_dom_state{}.
83
84%%----------------------------------------------------------------------
85%% Function: get_dom(State) -> Result
86%% Parameters:
87%% Result:
88%% Description:
89%%----------------------------------------------------------------------
90get_dom(#xmerl_sax_old_dom_state{dom=Dom}) ->
91    Dom.
92
93%%----------------------------------------------------------------------
94%% Function: event(Event, LineNo, State) -> Result
95%% Parameters:
96%% Result:
97%% Description:
98%%----------------------------------------------------------------------
99event(Event, _LineNo, State) ->
100    build_dom(Event, State).
101
102
103%%======================================================================
104%% Internal functions
105%%======================================================================
106
107%%----------------------------------------------------------------------
108%% Function  : build_dom(Event, State) -> Result
109%% Parameters: Event = term()
110%%             State = #xmerl_sax_old_dom_state{}
111%% Result    : #xmerl_sax_old_dom_state{} |
112%% Description:
113%%----------------------------------------------------------------------
114
115%% Document
116%%----------------------------------------------------------------------
117build_dom(startDocument, State) ->
118    State#xmerl_sax_old_dom_state{dom=[startDocument]};
119build_dom(endDocument,
120	  #xmerl_sax_old_dom_state{dom=[#xmlElement{content=C} = Current |D]} = State) ->
121    case D of
122	[startDocument] ->
123	    State#xmerl_sax_old_dom_state{dom=[Current#xmlElement{
124							    content=lists:reverse(C)
125							   }]};
126	[#xmlDecl{} = Decl, startDocument] ->
127	    State#xmerl_sax_old_dom_state{dom=[Decl, Current#xmlElement{
128						 content=lists:reverse(C)
129						}]};
130	_ ->
131            %% endDocument is also sent by the parser when a fault occur to tell
132            %% the event receiver that no more input will be sent
133	    State
134    end;
135
136%% Element
137%%----------------------------------------------------------------------
138build_dom({startElement, Uri, LocalName, QName, Attributes},
139	  #xmerl_sax_old_dom_state{tags=T, cno=CN, namespaces=NS, dom=D} = State) ->
140
141    A = parse_attributes(LocalName, Attributes),
142    {Num, NewCN} =
143	case CN of
144	    [] ->
145		{1, [1]};
146	    [ N |CNs] ->
147		{N, [1, N+1 |CNs]}
148	end,
149
150    NsInfo =
151	case QName of
152	    {[], _} -> [];
153	    QN -> QN
154	end,
155    NameAsAtom = convert_qname_to_atom(QName),
156
157    State#xmerl_sax_old_dom_state{tags=[{NameAsAtom, Num} |T],
158				  cno=NewCN,
159				  dom=[#xmlElement{name=NameAsAtom,
160						   expanded_name=NameAsAtom,
161						   nsinfo=NsInfo,
162						   namespace=#xmlNamespace{default=list_to_atom(Uri),
163									   nodes=NS},
164						   pos=Num,
165						   parents=T,
166						   attributes=lists:reverse(A),
167						   xmlbase="."
168						  } | D]};
169build_dom({endElement, _Uri, LocalName, QName},
170	  #xmerl_sax_old_dom_state{tags=[_ |T],
171				   cno=[_ |CN],
172				   dom=[#xmlElement{name=CName, content=C} = Current,
173					#xmlElement{content=PC} = Parent | D]} = State) ->
174    case convert_qname_to_atom(QName) of
175	CName ->
176	    State#xmerl_sax_old_dom_state{tags=T,
177					  cno=CN,
178					  dom=[Parent#xmlElement{
179						 content=[Current#xmlElement{
180							    content=lists:reverse(C)
181							   }
182							  |PC]
183						} | D]};
184	_ ->
185	    ?error("Got end of element: " ++ LocalName ++ " but expected: " ++
186		   Current#xmlElement.name)
187    end;
188
189%% Text
190%%----------------------------------------------------------------------
191build_dom({characters, String},
192	  #xmerl_sax_old_dom_state{tags=T,
193				   cno=[Num |CN],
194				   dom=[#xmlElement{content=C} = Current| D]} = State) ->
195    State#xmerl_sax_old_dom_state{cno=[Num+1 |CN],
196				  dom=[Current#xmlElement{content=[#xmlText{value=String, parents=T, pos=Num, type=text}
197								   |C]} | D]};
198build_dom({ignorableWhitespace, String},
199	  #xmerl_sax_old_dom_state{tags=T,
200				   cno=[Num |CN],
201				   dom=[#xmlElement{content=C} = Current| D]} = State) ->
202    State#xmerl_sax_old_dom_state{cno=[Num+1 |CN],
203				  dom=[Current#xmlElement{content=[#xmlText{value=String,
204									    parents=T, pos=Num,
205									    type=text}
206								   |C]} | D]};
207
208%% Comments
209%%----------------------------------------------------------------------
210build_dom({comment, String},
211	  #xmerl_sax_old_dom_state{tags=T,
212				   cno=[Num |CN],
213				   dom=[#xmlElement{content=C} = Current| D]} = State) ->
214    State#xmerl_sax_old_dom_state{cno=[Num+1 |CN],
215				  dom=[Current#xmlElement{content=[#xmlComment{parents=T, pos=Num, value=String}|C]} | D]};
216
217%% NameSpaces
218%%----------------------------------------------------------------------
219build_dom({startPrefixMapping, [], _Uri}, State) ->
220    State;
221build_dom({startPrefixMapping, Prefix, Uri},
222	  #xmerl_sax_old_dom_state{namespaces=NS} = State) ->
223    State#xmerl_sax_old_dom_state{namespaces=[{Prefix, list_to_atom(Uri)} |NS]};
224build_dom({endPrefixMapping, Prefix},
225	  #xmerl_sax_old_dom_state{namespaces=[{Prefix, _} |NS]} = State) ->
226    State#xmerl_sax_old_dom_state{namespaces=NS};
227
228%% Processing instructions
229%%----------------------------------------------------------------------
230build_dom({processingInstruction,"xml", PiData},
231	  #xmerl_sax_old_dom_state{dom=D} = State) ->
232    {Vsn, PiData1}  = find_and_remove_attribute("version", PiData, []),
233    {Enc, PiData2}  = find_and_remove_attribute("encoding", PiData1, []),
234    {Standalone, PiData3}  = find_and_remove_attribute("standalone", PiData2, yes),
235    State#xmerl_sax_old_dom_state{dom=[#xmlDecl{vsn=Vsn, encoding=Enc, standalone=Standalone, attributes=PiData3}| D]};
236build_dom({processingInstruction, PiTarget, PiData},
237	  #xmerl_sax_old_dom_state{cno=[Num |CN],
238				   dom=[#xmlElement{content=C} = Current| D]} = State) ->
239    State#xmerl_sax_old_dom_state{cno=[Num+1 |CN],
240				  dom=[Current#xmlElement{content=[#xmlPI{name=PiTarget,pos=Num, value=PiData}
241								   |C]} | D]};
242%% Default
243%%----------------------------------------------------------------------
244build_dom(_E, State) ->
245    State.
246
247
248%%----------------------------------------------------------------------
249%% Function  : parse_attributes(ElName, Attributes) -> Result
250%% Parameters:
251%% Result    :
252%% Description:
253%%----------------------------------------------------------------------
254parse_attributes(ElName, Attributes) ->
255    parse_attributes(ElName, Attributes, 1, []).
256
257parse_attributes(_, [], _, Acc) ->
258    Acc;
259parse_attributes(ElName, [{_Uri, Prefix, LocalName, AttrValue} |As], N, Acc) ->
260    Name = convert_qname_to_atom({Prefix,LocalName}),
261    NsInfo =
262	case Prefix of
263	    [] -> [];
264	    P -> {P,LocalName}
265	end,
266    parse_attributes(ElName, As, N+1, [#xmlAttribute{name=Name,
267						     pos=N,
268						     nsinfo=NsInfo,
269						     value=AttrValue,
270						     normalized=false} |Acc]).
271
272%%----------------------------------------------------------------------
273%% Function  : convert_qname_to_atom(QName) -> Result
274%% Parameters:
275%% Result    :
276%% Description:
277%%----------------------------------------------------------------------
278convert_qname_to_atom({[], N}) ->
279    list_to_atom(N);
280convert_qname_to_atom({P,N}) ->
281    list_to_atom(P ++ ":" ++ N).
282
283%%----------------------------------------------------------------------
284%% Function  : find_and_remove_attribute(Key, Data, Default) -> Result
285%% Parameters:
286%% Result    :
287%% Description:
288%%----------------------------------------------------------------------
289find_and_remove_attribute(Key, Data, Default) ->
290    case lists:keysearch(Key, 1, Data) of
291	{value, {Key, Value}} ->
292	    Data2 = lists:keydelete(Key, 1, Data),
293	    {Value, Data2};
294	false ->
295	    {Default, Data}
296    end.
297