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