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