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%% Description : Implements a search engine based on XPath 22 23%% @doc The xmerl_xpath module handles the entire XPath 1.0 spec. 24%% XPath expressions typically occur in XML attributes and are used to address 25%% parts of an XML document. 26% The grammar is defined in <code>xmerl_xpath_parse.yrl</code>. 27% The core functions are defined in <code>xmerl_xpath_pred.erl</code>. 28% 29% <p>Some useful shell commands for debugging the XPath parser</p> 30% <pre> 31% c(xmerl_xpath_scan). 32% yecc:yecc("xmerl_xpath_parse.yrl", "xmerl_xpath_parse", true, []). 33% c(xmerl_xpath_parse). 34% 35% xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("position() > -1")). 36% xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("5 * 6 div 2")). 37% xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("5 + 6 mod 2")). 38% xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("5 * 6")). 39% xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("-----6")). 40% xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("parent::node()")). 41% xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("descendant-or-self::node()")). 42% xmerl_xpath_parse:parse(xmerl_xpath_scan:tokens("parent::processing-instruction('foo')")). 43%% </pre> 44%% 45%% @type nodeEntity() = 46%% #xmlElement{} 47%% | #xmlAttribute{} 48%% | #xmlText{} 49%% | #xmlPI{} 50%% | #xmlComment{} 51%% | #xmlNsNode{} 52%% | #xmlDocument{} 53%% 54%% @type docNodes() = #xmlElement{} 55%% | #xmlAttribute{} 56%% | #xmlText{} 57%% | #xmlPI{} 58%% | #xmlComment{} 59%% | #xmlNsNode{} 60%% 61%% @type docEntity() = #xmlDocument{} | [docNodes()] 62%% 63%% @type xPathString() = string() 64%% 65%% @type parentList() = [{atom(), integer()}] 66%% 67%% @type option_list(). <p>Options allows to customize the behaviour of the 68%% XPath scanner. 69%% </p> 70%% <p> 71%% Possible options are: 72%% </p> 73%% <dl> 74%% <dt><code>{namespace, #xmlNamespace}</code></dt> 75%% <dd>Set namespace nodes, from XmlNamspace, in xmlContext</dd> 76%% <dt><code>{namespace, Nodes}</code></dt> 77%% <dd>Set namespace nodes in xmlContext.</dd> 78%% </dl> 79 80%% <dt><code>{bindings, Bs}</code></dt> 81%% <dd></dd> 82%% <dt><code>{functions, Fs}</code></dt> 83%% <dd></dd> 84-module(xmerl_xpath). 85 86 87%% main API 88-export([string/2, 89 string/3, 90 string/5]). 91 92%% exported helper functions, internal for the XPath support 93-export([eval_path/3, 94 axis/3, axis/4]). 95 96%% debug function 97-export([write_node/1]). 98 99 100-include("xmerl.hrl"). 101-include("xmerl_internal.hrl"). 102 103 104-record(state, {context = #xmlContext{}, 105 acc = []}). 106 107 108-define(nodeset(NS), #state{context = #xmlContext{nodeset = NS}}). 109-define(context(C), #state{context = C}). 110 111 112 113 114%% @spec string(Str, Doc) -> [docEntity()] | Scalar 115%% @equiv string(Str,Doc, []) 116string(Str, Doc) -> 117 string(Str, Doc, []). 118 119%% @spec string(Str,Doc,Options) -> 120%% [docEntity()] | Scalar 121%% @equiv string(Str,Doc, [],Doc,Options) 122string(Str, Doc, Options) -> 123 string(Str, Doc, [], Doc, Options). 124 125%% @spec string(Str,Node,Parents,Doc,Options) -> 126%% [docEntity()] | Scalar 127%% Str = xPathString() 128%% Node = nodeEntity() 129%% Parents = parentList() 130%% Doc = nodeEntity() 131%% Options = option_list() 132%% Scalar = #xmlObj{} 133%% @doc Extracts the nodes from the parsed XML tree according to XPath. 134%% xmlObj is a record with fields type and value, 135%% where type is boolean | number | string 136string(Str, Node, Parents, Doc, Options) -> 137%% record with fields type and value, 138%% where type is boolean | number | string 139 FullParents = 140 case Parents of 141 [] -> 142 []; 143 [{H, P}|_] when is_atom(H), is_integer(P) -> 144 full_parents(Parents, Doc) 145 end, 146%?dbg("string FullParents=~p~n",[FullParents]), 147 ContextNode=#xmlNode{type = node_type(Node), 148 node = Node, 149 parents = FullParents}, 150%?dbg("string ContextNode=~p~n",[ContextNode]), 151 WholeDoc = whole_document(Doc), 152%?dbg("string WholeDoc=~p~n",[WholeDoc]), 153 Context=(new_context(Options))#xmlContext{context_node = ContextNode, 154 whole_document = WholeDoc}, 155%?dbg("string Context=~p~n",[Context]), 156 #state{context = NewContext} = match(Str, #state{context = Context}), 157%?dbg("string NewContext=~p~n",[NewContext]), 158 case NewContext#xmlContext.nodeset of 159 ScalObj = #xmlObj{type=Scalar} 160 when Scalar == boolean; Scalar == number; Scalar == string -> 161 ScalObj; 162 #xmlObj{type=nodeset,value=NodeSet} -> 163 NodeSet; 164 _ -> 165 [N || #xmlNode{node = N} <- NewContext#xmlContext.nodeset] 166 end. 167 168 169whole_document(#xmlDocument{} = Doc) -> 170 #xmlNode{type = root_node, 171 node = Doc, 172 parents = []}; 173whole_document(Other) -> 174 #xmlNode{type = root_node, 175 node = #xmlDocument{content = Other}, 176 parents = []}. 177 178 179new_context(Options) -> 180 new_context(Options, #xmlContext{}). 181 182new_context([{namespace, #xmlNamespace{nodes = Nodes}}|T], C) -> 183 new_context(T, C#xmlContext{namespace = ns_nodes(Nodes)}); 184new_context([{namespace, Nodes}|T], C) -> 185 new_context(T, C#xmlContext{namespace = ns_nodes(Nodes)}); 186new_context([{bindings, Bs}|T], C) -> 187 new_context(T, C#xmlContext{bindings = Bs}); 188new_context([{functions, Fs}|T], C) -> 189 new_context(T, C#xmlContext{functions = Fs}); 190new_context([], C) -> 191 C. 192 193 194ns_nodes([{Prefix, URI}|T]) -> 195 [{to_string(Prefix), to_atom(URI)}|ns_nodes(T)]; 196ns_nodes([]) -> 197 []. 198 199full_parents(Ps, Doc) -> 200 full_parents1(lists:reverse(Ps), [Doc], []). 201 202full_parents1([{Name, Pos}|Ns], Content, Parents) -> 203 E = locate_element(Name, Pos, Content), 204 PN = #xmlNode{type = element, 205 node = E, 206 parents = Parents}, 207 full_parents1(Ns, get_content(E), [PN|Parents]); 208full_parents1([], _E, Parents) -> 209 Parents. 210 211 212locate_element(Name, Pos, [E = #xmlElement{name = Name, pos = Pos}|_]) -> 213 E; 214locate_element(_Name, Pos, [#xmlElement{pos = P}|_]) when P >= Pos -> 215 %% we've passed Pos (P > Pos) or the name is wrong (P == Pos) 216 exit(invalid_parents); 217locate_element(_Name, _Pos, []) -> 218 exit(invalid_parents); 219locate_element(Name, Pos, [_|T]) -> 220 locate_element(Name, Pos, T). 221 222 223match(Str, S = #state{}) -> 224 Tokens = xmerl_xpath_scan:tokens(Str), 225 case xmerl_xpath_parse:parse(Tokens) of 226 {ok, Expr} -> 227 match_expr(Expr, S); 228 Error -> 229 Error 230 end. 231 232 233match_expr({path, Type, Arg}, S) -> 234 eval_path(Type, Arg, S#state.context); 235%% PrimaryExpr 236match_expr(PrimExpr,S) -> 237 eval_primary_expr(PrimExpr,S). 238 239 240 241 242 243path_expr({refine, StepExpr1, StepExpr2}, S) -> 244 ?dbg("StepExpr1=~p StepExpr2=~p~n", [StepExpr1,StepExpr2]), 245 ?dbg("length(nodeset) = ~p~n", 246 [length((S#state.context)#xmlContext.nodeset)]), 247 S1 = path_expr(StepExpr1, S), 248 ?dbg("length(nodeset1) = ~p~n", 249 [length((S1#state.context)#xmlContext.nodeset)]), 250 path_expr(StepExpr2, S1); 251path_expr({step, {Axis, NodeTest, PredExpr}}, S = #state{context = C, 252 acc = Acc}) -> 253 ?dbg("PredExpr = ~p~n", [PredExpr]), 254 NewContext = axis(Axis, NodeTest, C, Acc), 255 pred_expr(PredExpr, S#state{context = NewContext}); 256path_expr('/', S) -> 257 S. 258 259 260pred_expr([], S) -> 261 S; 262pred_expr([{pred, Pred}|Preds], S = #state{}) -> 263 ?dbg("Pred = ~p~n", [Pred]), 264 NewS = eval_pred(Pred, S), 265 pred_expr(Preds, NewS). 266 267%% simple case: the predicate is a number, e.g. para[5]. 268%% No need to iterate over all nodes in the nodeset; we know what to do. 269%% 270eval_pred({number, N0}, 271 S = #state{context = C = #xmlContext{nodeset = NS, 272 axis_type = AxisType}}) -> 273 Len = length(NS), 274 case Len>=N0 of 275 true -> 276 N = case AxisType of 277 forward -> 278 N0; 279 reverse -> 280 Len + 1 - N0 281 end, 282 NewNodeSet = [lists:nth(N, NS)], 283 NewContext = C#xmlContext{nodeset = NewNodeSet}, 284 S#state{context = NewContext}; 285 false -> S#state{context = C#xmlContext{nodeset = []}} 286 end; 287eval_pred(Predicate, S = #state{context = C = 288 #xmlContext{nodeset = NodeSet}}) -> 289 NewNodeSet = 290 lists:filter( 291 fun(Node) -> 292 %?dbg("current node: ~p~n", [write_node(Node)]), 293 ThisContext = C#xmlContext{context_node = Node}, 294 xmerl_xpath_pred:eval(Predicate, ThisContext) 295 end, NodeSet), 296 NewContext = C#xmlContext{nodeset = NewNodeSet}, 297 S#state{context = NewContext}. 298 299 300 301%% write_node(Node::xmlNode()) -> {Type,Pos,Name,Parents} 302%% Helper function to access essential information from the xmlNode record. 303%% @hidden 304write_node(#xmlNode{pos = Pos, 305 node = #xmlAttribute{name = Name, 306 parents = Ps}}) -> 307 {attribute, Pos, Name, Ps}; 308write_node(#xmlNode{pos = Pos, 309 node = #xmlElement{name = Name, 310 parents = Ps}}) -> 311 {element, Pos, Name, Ps}; 312write_node(#xmlNode{pos = Pos, 313 node = #xmlText{value = Txt, 314 parents = Ps}}) -> 315 {text, Pos, Txt, Ps}; 316write_node(#xmlNode{pos = Pos, 317 node = #xmlComment{parents = Ps}}) -> 318 {comment, Pos, '', Ps}; 319write_node(#xmlNode{pos = Pos, 320 node = #xmlPI{name = Name, 321 parents = Ps}}) -> 322 {processing_instruction, Pos, Name, Ps}; 323write_node(#xmlNode{pos = Pos, 324 node = #xmlNsNode{parents = Ps, 325 prefix = Prefix}}) -> 326 {namespace, Pos, Prefix, Ps}; 327write_node(_) -> 328 other. 329 330 331%% eval_path(Type,Arg,S::state()) -> state() 332%% Eval path 333%% @hidden 334eval_path(union, {PathExpr1, PathExpr2}, C = #xmlContext{}) -> 335 S = #state{context = C}, 336 S1 = match_expr(PathExpr1, S), 337%% NewNodeSet = (S1#state.context)#xmlContext.nodeset, 338 S2 = match_expr(PathExpr2, S1#state{context=C}), 339 NodeSet1 = (S1#state.context)#xmlContext.nodeset, 340 NodeSet2 = (S2#state.context)#xmlContext.nodeset, 341 NewNodeSet = ordsets:to_list(ordsets:union(ordsets:from_list(NodeSet1), 342 ordsets:from_list(NodeSet2))), 343 S2#state{context=(S2#state.context)#xmlContext{nodeset=NewNodeSet}}; 344eval_path(abs, PathExpr, C = #xmlContext{}) -> 345 NodeSet = [C#xmlContext.whole_document], 346 Context = C#xmlContext{nodeset = NodeSet}, 347 S = #state{context = Context}, 348 path_expr(PathExpr, S); 349eval_path(rel, PathExpr, C = #xmlContext{}) -> 350 NodeSet = [C#xmlContext.context_node], 351 Context = C#xmlContext{nodeset = NodeSet}, 352 S = #state{context = Context}, 353 path_expr(PathExpr, S); 354eval_path(filter, {PathExpr, {pred, Pred}}, C = #xmlContext{}) -> 355 S = #state{context = C}, 356 S1 = match_expr(PathExpr, S), 357 eval_pred(Pred, S1). 358 359eval_primary_expr(PrimExpr, S = #state{context = Context}) -> 360%% NewNodeSet = xmerl_xpath_pred:eval(FC, Context), 361 NewNodeSet = xmerl_xpath_lib:eval(primary_expr, PrimExpr, Context), 362 NewContext = Context#xmlContext{nodeset = NewNodeSet}, 363 S#state{context = NewContext}. 364 365 366%% axis(Axis,NodeTest,Context::xmlContext()) -> xmlContext() 367%% axis(Axis,NodeTest,Context,[]) 368%% @hidden 369axis(Axis, NodeTest, Context) -> 370 axis(Axis, NodeTest, Context, []). 371 372 373%% axis(Axis,NodeTest,Context::xmlContext(),Acc) -> xmlContext() 374%% 375%% An axis specifies the tree relationship between the nodes selected by 376%% the location step and the context node. 377%% @hidden 378axis(Axis, NodeTest, Context = #xmlContext{nodeset = NS0}, Acc) -> 379 NewNodeSet=lists:foldr( 380 fun(N, AccX) -> 381 axis1(Axis, NodeTest, N, AccX, Context) 382 end, Acc, NS0), 383 update_nodeset(fwd_or_reverse(Axis, Context), NewNodeSet). 384 385 386axis1(self, Tok, N, Acc, Context) -> 387 match_self(Tok, N, Acc, Context); 388axis1(descendant, Tok, N, Acc, Context) -> 389 match_descendant(Tok, N, Acc, Context); 390axis1(child, Tok, N, Acc, Context) -> 391 match_child(Tok, N, Acc, Context); 392axis1(parent, Tok, N, Acc, Context) -> 393 match_parent(Tok, N, Acc, Context); 394axis1(ancestor, Tok, N, Acc, Context) -> 395 match_ancestor(Tok, N, Acc, Context); 396axis1(following_sibling, Tok, N, Acc, Context) -> 397 match_following_sibling(Tok, N, Acc, Context); 398axis1(preceding_sibling, Tok, N, Acc, Context) -> 399 match_preceding_sibling(Tok, N, Acc, Context); 400axis1(following, Tok, N, Acc, Context) -> 401 match_following(Tok, N, Acc, Context); 402axis1(preceding, Tok, N, Acc, Context) -> 403 match_preceding(Tok, N, Acc, Context); 404axis1(attribute, Tok, N, Acc, Context) -> 405 match_attribute(Tok, N, Acc, Context); 406axis1(namespace, Tok, N, Acc, Context) -> 407 match_namespace(Tok, N, Acc, Context); 408axis1(ancestor_or_self, Tok, N, Acc, Context) -> 409 match_ancestor_or_self(Tok, N, Acc, Context); 410axis1(descendant_or_self, Tok, N, Acc, Context) -> 411 match_descendant_or_self(Tok, N, Acc, Context). 412 413 414fwd_or_reverse(ancestor, Context) -> 415 reverse_axis(Context); 416fwd_or_reverse(ancestor_or_self, Context) -> 417 reverse_axis(Context); 418fwd_or_reverse(preceding_sibling, Context) -> 419 reverse_axis(Context); 420fwd_or_reverse(preceding, Context) -> 421 reverse_axis(Context); 422fwd_or_reverse(_, Context) -> 423 forward_axis(Context). 424 425reverse_axis(Context) -> 426 Context#xmlContext{axis_type = reverse}. 427forward_axis(Context) -> 428 Context#xmlContext{axis_type = forward}. 429 430 431 432match_self(Tok, N, Acc, Context) -> 433 case node_test(Tok, N, Context) of 434 true -> 435 [N|Acc]; 436 false -> 437 Acc 438 end. 439 440 441match_descendant(Tok, N, Acc, Context) -> 442 #xmlNode{parents = Ps, node = Node, type = Type} = N, 443 case Type of 444 El when El == element; El == root_node -> 445 NewPs = [N|Ps], 446 match_desc(get_content(Node), NewPs, Tok, Acc, Context); 447 _Other -> 448 Acc 449 end. 450 451 452match_desc([E = #xmlElement{}|T], Parents, Tok, Acc, Context) -> 453 Acc1 = match_desc(T, Parents, Tok, Acc, Context), 454 N = #xmlNode{type = node_type(E), 455 node = E, 456 parents = Parents}, 457 NewParents = [N|Parents], 458 Acc2 = match_desc(get_content(E), NewParents, Tok, Acc1, Context), 459 match_self(Tok, N, Acc2, Context); 460match_desc([E|T], Parents, Tok, Acc, Context) -> 461 Acc1 = match_desc(T, Parents, Tok, Acc, Context), 462 N = #xmlNode{node = E, 463 type = node_type(E), 464 parents = Parents}, 465 match_self(Tok, N, Acc1, Context); 466match_desc([], _Parents, _Tok, Acc, _Context) -> 467 Acc. 468 469 470 471%% "The 'descendant-or-self' axis contains the context node and the 472%% descendants of the context node." 473match_descendant_or_self(Tok, N, Acc, Context) -> 474 Acc1 = match_descendant(Tok, N, Acc, Context), 475 match_self(Tok, N, Acc1, Context). 476 477 478match_child(Tok, N, Acc, Context) -> 479 %?dbg("match_child(~p)~n", [write_node(N)]), 480 #xmlNode{parents = Ps, node = Node, type = Type} = N, 481 case Type of 482 El when El == element; El == root_node -> 483 NewPs = [N|Ps], 484 lists:foldr( 485 fun(E, AccX) -> 486 ThisN = #xmlNode{type = node_type(E), 487 node = E, 488 parents = NewPs}, 489 match_self(Tok, ThisN, AccX, Context) 490 end, Acc, get_content(Node)); 491 _Other -> 492 Acc 493 end. 494 495 496%% "The 'parent' axis contains the parent of the context node, 497%% if there is one." 498match_parent(Tok, N, Acc, Context) -> 499 case N#xmlNode.parents of 500 [] -> 501 Acc; 502 [PN|_] -> 503 match_self(Tok, PN, Acc, Context) 504 end. 505 506 507%% "The 'ancestor' axis contains the ancestors of the context node; 508%% the ancestors of the context node consists of the parent of the context 509%% node and the parent's parent and so on; thus, the ancestor axis will 510%% always include the root node, unless the context node is the root node." 511match_ancestor(Tok, N, Acc, Context) -> 512 Parents = N#xmlNode.parents, 513 lists:foldl( 514 fun(PN, AccX) -> 515 match_self(Tok, PN, AccX, Context) 516 end, Acc, Parents). 517 518 519 520 521%% "The 'ancestor-or-self' axis contains the context node and the ancestors 522%% of the context node; thus, the acestor axis will always include the 523%% root node." 524match_ancestor_or_self(Tok, N, Acc, Context) -> 525 Acc1 = match_self(Tok, N, Acc, Context), 526 match_ancestor(Tok, N, Acc1, Context). 527 528 529match_following_sibling(_Tok, #xmlAttribute{}, Acc, _Context) -> 530 Acc; 531match_following_sibling(_Tok, #xmlNamespace{}, Acc, _Context) -> 532 Acc; 533 534match_following_sibling(Tok, N, Acc, Context) -> 535 #xmlNode{parents = Ps, node = Node} = N, 536 case Ps of 537 [#xmlNode{type = element, 538 node = #xmlElement{} = PNode}|_] -> 539 FollowingSiblings = lists:nthtail(get_position(Node), 540 get_content(PNode)), 541 lists:foldr( 542 fun(E, AccX) -> 543 ThisN = #xmlNode{type = node_type(E), 544 node = E, 545 parents = Ps}, 546 match_self(Tok, ThisN, AccX, Context) 547 end, Acc, FollowingSiblings); 548 _Other -> 549 Acc 550 end. 551 552 553%% "The 'following' axis contains all nodes in the same document as the 554%% context node that are after the context node in document order, excluding 555%% any descendants and excluding attribute nodes and namespace nodes." 556match_following(Tok, N, Acc, Context) -> 557 #xmlNode{parents = Ps, node = Node} = N, 558 case Ps of 559 [#xmlNode{type = element, 560 node = #xmlElement{} = PNode} = P|_] -> 561 FollowingSiblings = lists:nthtail(get_position(Node), 562 get_content(PNode)), 563 Acc0 = match_following(Tok, P, Acc, Context), 564 lists:foldr( 565 fun(E, AccX) -> 566 ThisN = #xmlNode{type = node_type(E), 567 node = E, 568 parents = Ps}, 569 match_descendant_or_self(Tok, ThisN, AccX, Context) 570 end, Acc0, FollowingSiblings); 571 _Other -> 572 Acc 573 end. 574 575 576%% "The preceding-sibling axis contains all the preceding siblings of the 577%% context node; if the context node is an attribute node or namespace node, 578%% the preceding-sibling axis is empty." 579match_preceding_sibling(_Tok, #xmlAttribute{}, Acc, _Context) -> 580 Acc; 581match_preceding_sibling(_Tok, #xmlNamespace{}, Acc, _Context) -> 582 Acc; 583 584match_preceding_sibling(Tok, N, Acc, Context) -> 585 #xmlNode{parents = Ps, node = Node} = N, 586 case Ps of 587 [#xmlNode{type = element, 588 node = #xmlElement{} = PNode}|_] -> 589 PrecedingSiblings = lists:sublist(get_content(PNode), 1, 590 get_position(Node) - 1), 591 lists:foldr( 592 fun(E, AccX) -> 593 ThisN = #xmlNode{type = node_type(E), 594 node = E, 595 parents = Ps}, 596 match_self(Tok, ThisN, AccX, Context) 597 end, Acc, PrecedingSiblings); 598 _Other -> 599 Acc 600 end. 601 602 603%% "The 'preceding' axis contains all nodes in the same document as the context 604%% node that are before the context node in document order, exluding any 605%% ancestors and excluding attribute nodes and namespace nodes." 606match_preceding(Tok, N, Acc, Context) -> 607 #xmlNode{parents = Ps, node = Node} = N, 608 case Ps of 609 [#xmlNode{type = element, 610 node = #xmlElement{} = PNode} = P|_] -> 611 PrecedingSiblings = lists:sublist(get_content(PNode), 1, 612 get_position(Node) - 1), 613 Acc0 = lists:foldr( 614 fun(E, AccX) -> 615 ThisN = #xmlNode{type = node_type(E), 616 node = E, 617 parents = Ps}, 618 match_descendant_or_self(Tok, ThisN, 619 AccX, Context) 620 end, Acc, PrecedingSiblings), 621 match_preceding(Tok, P, Acc0, Context); 622 _Other -> 623 Acc 624 end. 625 626 627%% "The 'attribute' axis contains the attributes of the context node; the 628%% axis will be empty unless the context node is an element." 629match_attribute(Tok, N, Acc, Context) -> 630 case N#xmlNode.type of 631 element -> 632 #xmlNode{parents = Ps, node = E} = N, 633 lists:foldr( 634 fun(A, AccX) -> 635 ThisN = #xmlNode{type = attribute, 636 node = A, 637 parents = [N|Ps]}, 638 match_self(Tok, ThisN, AccX, Context) 639 end, Acc, E#xmlElement.attributes); 640 _Other -> 641 %%[] 642 Acc 643 end. 644 645node_type(#xmlAttribute{}) -> attribute; 646node_type(#xmlElement{}) -> element; 647node_type(#xmlText{}) -> text; 648node_type(#xmlPI{}) -> processing_instruction; 649node_type(#xmlNsNode{}) -> namespace; 650node_type(#xmlComment{}) -> comment; 651node_type(#xmlDocument{}) -> root_node. 652 653%% "The namespace axis contains the namespace nodes of the context node; 654%% the axis will be empty unless the context node is an element." 655match_namespace(Tok, N, Acc, Context) -> 656 case N#xmlNode.type of 657 element -> 658 #xmlNode{parents = Ps, node = E} = N, 659 #xmlElement{name = Name, 660 namespace = NS, 661 parents = EPs, 662 pos = Pos} = E, 663 #xmlNamespace{default = Default, nodes = NSPairs} = NS, 664 ThisEPs = [{Name, Pos}|EPs], 665 ThisPs = [N|Ps], 666 Acc0 = 667 case Default of 668 D when D =:= []; D =:= '' -> 669 {[], 1}; 670 URI -> 671 DefaultNSNode = #xmlNsNode{parents = ThisEPs, 672 pos = 1, 673 prefix = [], 674 uri = URI}, 675 Node = #xmlNode{type = namespace, 676 node = DefaultNSNode, 677 parents = ThisPs}, 678 {[Node], 2} 679 end, 680 {Nodes, _I} = 681 lists:foldr( 682 fun ({Prefix, URI}, {AccX, I}) -> 683 NSNode = #xmlNsNode{parents = ThisEPs, 684 pos = I, 685 prefix = Prefix, 686 uri = URI}, 687 ThisN = #xmlNode{pos = I, 688 type = namespace, 689 node = NSNode, 690 parents = ThisPs}, 691 {[ThisN | AccX], I + 1} 692 end, Acc0, NSPairs), 693 lists:foldr( 694 fun (ThisN, AccX) -> 695 match_self(Tok, ThisN, AccX, Context) 696 end, Acc, Nodes); 697 _Other -> 698 %%[] 699 Acc 700 end. 701 702 703update_nodeset(Context = #xmlContext{axis_type = AxisType}, NodeSet) -> 704 MapFold = 705 case AxisType of 706 forward -> 707 mapfoldl; 708 reverse -> 709 mapfoldr 710 end, 711 {Result, _N} = 712 lists:MapFold(fun(Node, N) -> 713 {Node#xmlNode{pos = N}, N + 1} 714 end, 1, NodeSet), 715 Context#xmlContext{nodeset = Result}. 716 717 718 719node_test(F, N, Context) when is_function(F) -> 720 F(N, Context); 721node_test(_Test, #xmlNode{type=attribute,node=#xmlAttribute{name=xmlns}}, 722 _Context) -> 723 false; 724node_test(_Test, 725 #xmlNode{type=attribute,node=#xmlAttribute{nsinfo={"xmlns",_Local}}}, 726 _Context) -> 727 false; 728node_test({wildcard, _}, #xmlNode{type=ElAt}, _Context) 729 when ElAt==element; ElAt==attribute; ElAt==namespace -> 730 true; 731node_test({prefix_test, Prefix}, #xmlNode{node = N}, Context) -> 732 case N of 733 #xmlElement{nsinfo = {Prefix, _}} -> 734 true; 735 #xmlElement{expanded_name = {Uri, _}} -> 736 case expanded_name(Prefix, "_", Context) of 737 {Uri, _} -> 738 true; 739 _ -> 740 false 741 end; 742 #xmlAttribute{nsinfo = {Prefix, _}} -> 743 true; 744 #xmlAttribute{expanded_name = {Uri, _}} -> 745 case expanded_name(Prefix, "_", Context) of 746 {Uri, _} -> 747 true; 748 _ -> 749 false 750 end; 751 _ -> 752 false 753 end; 754node_test({name, {Tag, _Prefix, _Local}}, 755 #xmlNode{node = #xmlElement{name = Tag}}=_N, _Context) -> 756 %?dbg("node_test({tag, ~p}, ~p) -> true.~n", [Tag, write_node(_N)]), 757 true; 758node_test({name, {Tag, Prefix, Local}}, 759 #xmlNode{node = #xmlElement{name = Name, 760 expanded_name = EExpName, 761 nsinfo = {_Prefix1, _} 762 }}, Context) -> 763 case expanded_name(Prefix, Local, Context) of 764 [] -> 765 Res = (Tag == Name), 766 ?dbg("node_test(~p, ~p) -> ~p.~n", 767 [{Tag, Prefix, Local}, write_node(Name), Res]), 768 Res; 769 ExpName -> 770 Res = (ExpName == EExpName), 771 ?dbg("node_test(~p, ~p) -> ~p.~n", 772 [{Tag, Prefix, Local}, write_node(Name), Res]), 773 Res 774 end; 775node_test({name, {_Tag, Prefix, Local}}, 776 #xmlNode{node = #xmlElement{name = Name, 777 expanded_name = _EExpName, 778 namespace = NS 779 }}, Context) -> 780 case expanded_name(Prefix, Local, Context) of 781 [] -> 782 ?dbg("node_test(~p, ~p) -> ~p.~n", 783 [{_Tag, Prefix, Local}, write_node(Name), false]), 784 false; 785 ExpName -> 786 Res = (ExpName == {NS#xmlNamespace.default,Name}), 787 ?dbg("node_test(~p, ~p) -> ~p.~n", 788 [{_Tag, Prefix, Local}, write_node(Name), Res]), 789 Res 790 end; 791node_test({name, {Tag,_Prefix,_Local}}, 792 #xmlNode{node = #xmlAttribute{name = Tag}}, _Context) -> 793 true; 794node_test({name, {Tag, Prefix, Local}}, 795 #xmlNode{node = #xmlAttribute{name = Name, 796 expanded_name = EExpName 797 }}, Context) -> 798 case expanded_name(Prefix, Local, Context) of 799 [] -> 800 Res = (Tag == Name), 801 ?dbg("node_test(~p, ~p) -> ~p.~n", 802 [{Tag, Prefix, Local}, write_node(Name), Res]), 803 Res; 804 ExpName -> 805 Res = (ExpName == EExpName), 806 ?dbg("node_test(~p, ~p) -> ~p.~n", 807 [{Tag, Prefix, Local}, write_node(Name), Res]), 808 Res 809 end; 810node_test({name, {_Tag, [], Local}}, 811 #xmlNode{node = #xmlNsNode{prefix = Local}}, _Context) -> 812 true; 813node_test({node_type, NT}, #xmlNode{node = N}, _Context) -> 814 case {NT, N} of 815 {text, #xmlText{}} -> 816 true; 817 {node, _} -> 818 true; 819 {attribute, #xmlAttribute{}} -> 820 true; 821 {namespace, #xmlNsNode{}} -> 822 true; 823 {comment, #xmlComment{}} -> 824 true; 825 {processing_instruction, #xmlPI{}} -> 826 true; 827 _ -> 828 false 829 end; 830node_test({processing_instruction, Name1}, 831 #xmlNode{node = #xmlPI{name = Name2}}, _Context) -> 832 Name1 == atom_to_list(Name2); 833node_test(_Other, _N, _Context) -> 834 %?dbg("node_test(~p, ~p) -> false.~n", [_Other, write_node(_N)]), 835 false. 836 837 838expanded_name(Prefix, Local, #xmlContext{namespace = NS}) -> 839 case lists:keysearch(Prefix, 1, NS) of 840 {value, {_, URI}} -> 841 {URI, list_to_atom(Local)}; 842 false -> 843 [] 844 end. 845 846 847to_atom(A) when is_atom(A) -> A; 848to_atom(S) when is_list(S) -> list_to_atom(S). 849 850to_string(A) when is_atom(A) -> atom_to_list(A); 851to_string(S) when is_list(S) -> S. 852 853 854get_content(#xmlElement{content = C}) when is_list(C) -> 855 C; 856get_content(#xmlElement{content = F} = E) when is_function(F) -> 857 case F() of 858 C when is_list(C) -> 859 C; 860 _Other -> 861 exit({bad_content, E}) 862 end; 863get_content(#xmlDocument{content = C}) when is_list(C) -> 864 C; 865get_content(#xmlDocument{content = C}) -> 866 [C]. 867 868 869get_position(#xmlElement{pos = N}) -> 870 N; 871get_position(#xmlText{pos = N}) -> 872 N. 873