%% ------------------------------------------------------------------- %% %% xqerl - XQuery processor %% %% Copyright (c) 2018-2020 Zachary N. Dean All Rights Reserved. %% %% This file is provided to you 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. %% %% ------------------------------------------------------------------- %% XXX Here, make a new function that builds chained XPaths into one function. %% also, add functions for DB nodes. DB nodes will have a different ID to %% tell them apart from in-memory nodes. %% Also need a grouping function for node lists. Group by DB id. -module(xqldb_xpath). -compile( {inline, [ name_type_match/3, name_match/2 ]} ). %% ========================================================================== %% API functions %% ========================================================================== -export([ id/2, idref/2, base_uri/1, document_uri/1, lang/1, namespace_nodes/1 ]). -export([ document_order/1, simple_path/2 ]). -export([ ancestor_document_node/2, ancestor_element/2, ancestor_node/2, ancestor_or_self_attribute/2, ancestor_or_self_comment/2, ancestor_or_self_document_node/2, ancestor_or_self_element/2, ancestor_or_self_node/2, ancestor_or_self_processing_instruction/2, ancestor_or_self_text/2, attribute_attribute/2, attribute_node/2, child_comment/2, child_element/2, child_node/2, child_processing_instruction/2, child_text/2, descendant_comment/2, descendant_element/2, descendant_node/2, descendant_or_self_attribute/2, descendant_or_self_comment/2, descendant_or_self_document_node/2, descendant_or_self_element/2, descendant_or_self_node/2, descendant_or_self_processing_instruction/2, descendant_or_self_text/2, descendant_processing_instruction/2, descendant_text/2, following_comment/2, following_element/2, following_node/2, following_processing_instruction/2, following_sibling_comment/2, following_sibling_element/2, following_sibling_node/2, following_sibling_processing_instruction/2, following_sibling_text/2, following_text/2, parent_document_node/2, parent_element/2, parent_node/2, preceding_comment/2, preceding_element/2, preceding_node/2, preceding_processing_instruction/2, preceding_sibling_comment/2, preceding_sibling_element/2, preceding_sibling_node/2, preceding_sibling_processing_instruction/2, preceding_sibling_text/2, preceding_text/2, self_attribute/2, self_comment/2, self_document_node/2, self_element/2, self_namespace/2, self_node/2, self_processing_instruction/2, self_text/2 ]). -type attribute() :: #{'nk' := attribute, _ => _}. -type comment() :: #{'nk' := comment, _ => _}. -type document() :: #{'nk' := document, _ => _}. -type element() :: #{'nk' := element, _ => _}. -type proc_inst() :: #{'nk' := 'processing-instruction', _ => _}. -type namespace() :: #{'nk' := namespace, _ => _}. -type text() :: #{'nk' := text, _ => _}. -type anynode() :: attribute() | comment() | document() | element() | proc_inst() | text(). -type anychild() :: comment() | element() | proc_inst() | text(). % Name for element/attribute and processing-instruction -type name() :: {Namespace :: '_' | binary(), LocalName :: '_' | binary(), Type :: atom()} | {LocalName :: '_' | binary()}. -type predicate() :: fun( ( Node :: anynode(), Position :: non_neg_integer(), Last :: non_neg_integer() ) -> boolean() ). % for named node kinds -type step() :: {Name :: name(), Predicates :: [predicate()]} | {Predicates :: [predicate()]}. -define(DB_ND, {_, _, _}). %% NodeName is {Ns,Prefix,Local} id(DocumentNode, Vals) -> Eq = fun (#{id := ?DB_ND} = A, _, _) -> lists:member(xqldb_nodes:string_value(A), Vals); (A, _, _) -> lists:member(xqldb_mem_nodes:string_value(A), Vals) end, Pred = fun(E, _, _) -> N = attribute_attribute( E, {{<<"http://www.w3.org/XML/1998/namespace">>, <<"id">>, '_'}, [Eq]} ), T = attribute_attribute(E, {{'_', '_', 'xs:ID'}, [Eq]}), N =/= [] orelse T =/= [] end, Elems = descendant_element(DocumentNode, {{'_', '_', '_'}, [Pred]}), document_order(Elems). idref(DocumentNode, Vals) -> Eq = fun (#{id := {_, _, _}} = A, _, _) -> F = fun(V) -> lists:member(V, Vals) end, lists:any(F, string:split(xqldb_nodes:string_value(A), " ", all)); (A, _, _) -> F = fun(V) -> lists:member(V, Vals) end, lists:any(F, string:split(xqldb_mem_nodes:string_value(A), " ", all)) end, Pred = fun(E, _, _) -> T = self_attribute(E, {{'_', '_', 'xs:IDREF'}, [Eq]}), T =/= [] end, Elems = descendant_element(DocumentNode, {{'_', '_', '_'}, []}), Atts = [ A || E <- Elems, A <- attribute_node(E, {[Pred]}) ], document_order(Atts). namespace_nodes(#{id := ?DB_ND} = Node) -> xqldb_nodes:namespace_nodes(Node); namespace_nodes(Node) -> xqldb_mem_nodes:namespace_nodes(Node). document_uri(#{id := ?DB_ND} = Node) -> xqldb_nodes:document_uri(Node); document_uri(Node) -> xqldb_mem_nodes:document_uri(Node). base_uri(#{id := ?DB_ND} = Node) -> xqldb_nodes:base_uri(Node); base_uri(Node) -> xqldb_mem_nodes:base_uri(Node). lang(Node) -> Langs = [ L || A <- ancestor_or_self_node(Node, {[]}), L <- attribute_attribute( A, {{<<"http://www.w3.org/XML/1998/namespace">>, <<"lang">>, '_'}, []} ) ], case lists:reverse(Langs) of [H | _] -> xqldb_mem_nodes:string_value(H); _ -> [] end. % when given a unique list of nodes, returns the results of all % Steps in document order. simple_path([InitCtxItems], [{'following-sibling', _}] = Steps) -> Db = get_group_key(InitCtxItems), select_fun(Db, [InitCtxItems], Steps); simple_path(InitCtxItems, Steps) when is_list(InitCtxItems) -> MergedNodes = merge_nodes(InitCtxItems), %?dbg("MergedNodes", MergedNodes), % group IDs by Type and DB % mem nodes all together % DB nodes grouped by DB then by DocId % mem nodes means finding each fun for each step without predicates % then building a recursive list comprehension for it All = case MergedNodes of [{Db, Nodes}] -> [select_fun(Db, Nodes, Steps)]; _ -> select_fun_p(MergedNodes, Steps) end, document_order(lists:append(All)); simple_path(InitCtxItems, Steps) -> simple_path([InitCtxItems], Steps). simple_descendant_path(A, B, P) -> simple_path(P, [{descendant, {A, B}}]). select_fun_p(MergedNodes, Steps) -> Self = self(), Pids = [ begin G = fun() -> Self ! {self(), select_fun(Db, Nodes, Steps)} end, spawn(node(DbPid), G) end || {{DbPid, _, _} = Db, Nodes} <- MergedNodes ], %?dbg("count Pids", length(Pids)), collect_select_funs(Pids, []). collect_select_funs([Pid | T], Acc) -> receive {Pid, V} -> collect_select_funs(T, [V | Acc]) after 5000 -> throw({error, timeout}) end; collect_select_funs([], Acc) -> Acc. merge_nodes([N]) -> Key = get_group_key(N), [{Key, [N]}]; merge_nodes(Nodes) -> merge_nodes(Nodes, #{}). % merges nodes by DB, node output is reversed merge_nodes([], Map) -> maps:to_list(Map); merge_nodes([[N] | T], Map) -> merge_nodes([N | T], Map); merge_nodes([N | T], Map) -> Key = get_group_key(N), case Map of #{Key := Ns} -> merge_nodes(T, Map#{Key := [N | Ns]}); _ -> merge_nodes(T, Map#{Key => [N]}) end. -compile({inline, {get_group_key, 1}}). get_group_key(#{id := {Ref, _Id}}) when is_reference(Ref) -> mem; get_group_key(#{ id := {Pid, DocId, _Id}, pa := PathId }) when is_pid(Pid) -> {Pid, DocId, PathId}; get_group_key(_) -> throw(xqerl_error:error('XPTY0019')). %% do_predicates(Ds, Preds) select_fun(mem, Nodes, Steps) -> Fun = build_mem_node_fun(Steps), lists:flatmap(Fun, Nodes); select_fun({DbPid, DocId, InPathId}, Nodes, Steps) -> DB = xqerl_context:get_db(DbPid), ResultSet = case xqldb_query_server:get(DB, DocId, InPathId, Steps) of undefined -> #{names := NameMap} = DB, Steps1 = normalize_step_names(Steps, NameMap), PathLookup = xqldb_structure_index:compile_path(DB, Steps1), Iters = [ xqldb_idx_mi:lookup_path(DB, DocId, OutPathId) || OutPathId <- PathLookup(InPathId) ], IterUnion = xqldb_join:union(Iters), Results = case lists:last(Steps) of atomize -> xqldb_nodes:iterator_to_atom_set(IterUnion, DB); double -> xqldb_nodes:iterator_to_dbl_set(IterUnion, DB); _ -> xqldb_nodes:iterator_to_node_set(IterUnion, DB) end, xqldb_query_server:put(DB, DocId, InPathId, Steps, Results), Results; Results -> Results end, EachNode = fun(#{id := {_, _, NodeId}}) -> case Steps of [{'following-sibling', _}] -> xqldb_nodes:select_following_siblings(ResultSet, NodeId); [{'preceding-sibling', _}] -> xqldb_nodes:select_preceding_siblings(ResultSet, NodeId); _ -> xqldb_nodes:select_with_prefix(ResultSet, NodeId) end end, case Nodes of [Node] -> EachNode(Node); _ -> lists:flatmap(EachNode, Nodes) end. normalize_step_names([atomize | T], NameMap) -> normalize_step_names(T, NameMap); normalize_step_names([double | T], NameMap) -> normalize_step_names(T, NameMap); normalize_step_names([{Ax, H} | T], NameMap) -> NewH = case H of {att, A, B} -> {att, lookup_name(A, NameMap), lookup_name(B, NameMap)}; {pi, B} -> {pi, lookup_name(B, NameMap)}; {A, B} -> {lookup_name(A, NameMap), lookup_name(B, NameMap)}; Other -> Other end, [{Ax, NewH} | normalize_step_names(T, NameMap)]; normalize_step_names([], _) -> []. lookup_name('_', _) -> '_'; lookup_name(Key, Map) -> case Map of #{Key := Val} -> Val; _ -> error end. %% Each path function takes one node and returns a list of nodes. %% ========================== %% ancestor - reverse %% ========================== -spec ancestor_document_node(attribute() | comment() | element() | proc_inst() | text(), step()) -> [document()]. ancestor_document_node( #{ id := {DbPid, DocId, _}, nk := Nk }, Step ) when Nk =/= document -> DocNode = xqldb_nodes:get_doc({DbPid, DocId, <<>>}), [ S || #{nk := document} = D <- [DocNode], S <- self_document_node(D, Step) ]; ancestor_document_node(#{nk := Nk} = Node, Step) when Nk =/= document -> [ S || #{nk := document} = D <- xqldb_mem_nodes:ancestors(Node), S <- self_document_node(D, Step) ]; ancestor_document_node(#{nk := _}, _) -> []. -spec ancestor_element(attribute() | comment() | element() | proc_inst() | text(), step()) -> [element()]. ancestor_element( #{ id := ?DB_ND, nk := Nk } = Node, {NameAndType, Preds} ) when Nk =/= document -> As = [ D || #{ nk := element, nn := NodeName } = D <- xqldb_nodes:ancestors(Node), name_type_match(NameAndType, NodeName, '_') ], Fil = do_predicates(As, Preds), lists:reverse(Fil); ancestor_element(#{nk := Nk} = Node, {NameAndType, Preds}) when Nk =/= document -> As = [ D || #{ nk := element, nn := NodeName, tn := TypeName } = D <- xqldb_mem_nodes:ancestors(Node), name_type_match(NameAndType, NodeName, TypeName) ], Fil = do_predicates(As, Preds), lists:reverse(Fil); ancestor_element(#{nk := _}, _) -> []. -spec ancestor_node(attribute() | comment() | element() | proc_inst() | text(), step()) -> [document() | element()]. ancestor_node( #{ id := ?DB_ND, nk := Nk } = Node, {Preds} ) when Nk =/= document -> Fil = do_predicates(xqldb_nodes:ancestors(Node), Preds), lists:reverse(Fil); ancestor_node(#{nk := Nk} = Node, {Preds}) when Nk =/= document -> Fil = do_predicates(xqldb_mem_nodes:ancestors(Node), Preds), lists:reverse(Fil); ancestor_node(#{nk := _}, _) -> []. %% ================================ %% ancestor_or_self - reverse %% ================================ -spec ancestor_or_self_attribute(attribute(), step()) -> [attribute()]. ancestor_or_self_attribute(#{nk := attribute} = Node, Step) -> self_attribute(Node, Step); ancestor_or_self_attribute(#{nk := _}, _) -> []. -spec ancestor_or_self_comment(comment(), step()) -> [comment()]. ancestor_or_self_comment(#{nk := comment} = Node, Step) -> self_comment(Node, Step); ancestor_or_self_comment(#{nk := _}, _) -> []. -spec ancestor_or_self_document_node( attribute() | comment() | document() | element() | proc_inst() | text(), step() ) -> [document()]. ancestor_or_self_document_node(#{id := ?DB_ND} = Node, Step) -> [ S || #{nk := document} = D <- [Node | xqldb_nodes:ancestors(Node)], S <- self_document_node(D, Step) ]; ancestor_or_self_document_node(Node, Step) -> [ S || #{nk := document} = D <- [Node | xqldb_mem_nodes:ancestors(Node)], S <- self_document_node(D, Step) ]. -spec ancestor_or_self_element( attribute() | comment() | element() | proc_inst() | text(), step() ) -> [element()]. ancestor_or_self_element( #{ id := ?DB_ND, nk := Nk } = Node, {NameAndType, Preds} ) when Nk =/= document -> As = [ D || #{ nk := element, nn := NodeName } = D <- [Node | xqldb_nodes:ancestors(Node)], name_type_match(NameAndType, NodeName, '_') ], Fil = do_predicates(As, Preds), lists:reverse(Fil); ancestor_or_self_element(#{nk := Nk} = Node, {NameAndType, Preds}) when Nk =/= document -> As = [ D || #{ nk := element, nn := NodeName, tn := TypeName } = D <- [Node | xqldb_mem_nodes:ancestors(Node)], name_type_match(NameAndType, NodeName, TypeName) ], Fil = do_predicates(As, Preds), lists:reverse(Fil); ancestor_or_self_element(#{nk := _}, _) -> []. -spec ancestor_or_self_node( attribute() | comment() | document() | element() | proc_inst() | text(), step() ) -> [anynode()]. ancestor_or_self_node(#{id := ?DB_ND} = Node, {Preds}) -> As = [Node | xqldb_nodes:ancestors(Node)], Fil = do_predicates(As, Preds), lists:reverse(Fil); ancestor_or_self_node(Node, {Preds}) -> As = [Node | xqldb_mem_nodes:ancestors(Node)], Fil = do_predicates(As, Preds), lists:reverse(Fil). -spec ancestor_or_self_processing_instruction(proc_inst(), step()) -> [proc_inst()]. ancestor_or_self_processing_instruction(#{nk := 'processing-instruction'} = Node, Step) -> self_processing_instruction(Node, Step); ancestor_or_self_processing_instruction(#{nk := _}, _) -> []. -spec ancestor_or_self_text(text(), step()) -> [text()]. ancestor_or_self_text(#{nk := text} = Node, Step) -> self_text(Node, Step); ancestor_or_self_text(#{nk := _}, _) -> []. %% ================================ %% attribute %% ================================ -spec attribute_attribute(element(), step()) -> [attribute()]. attribute_attribute(#{nk := element, at := []}, _) -> []; attribute_attribute( #{ id := ?DB_ND, nk := element, at := _ } = P, {{A, B, T}, Preds} ) -> Cn = [ N#{pt => P} || #{ nk := attribute, tn := TypeName } = N <- simple_path(P, [{attribute, {att, A, B}}]), type_match(T, TypeName) ], do_predicates(Cn, Preds); attribute_attribute( #{ nk := element, at := At } = P, {NameAndType, Preds} ) -> Cn = [ N#{pt => P} || #{ nk := attribute, nn := NodeName, tn := TypeName } = N <- At, name_type_match(NameAndType, NodeName, TypeName) ], do_predicates(Cn, Preds); attribute_attribute(#{nk := _}, _) -> []. -spec attribute_node(element(), step()) -> [attribute()]. attribute_node(#{nk := element, at := []}, _) -> []; attribute_node( #{ id := ?DB_ND, nk := element, at := _ } = P, {Preds} ) -> do_predicates( [ N#{pt => P} || N <- simple_path(P, [{attribute, {att, '_', '_'}}]) ], Preds ); attribute_node( #{ nk := element, at := At } = P, {Preds} ) -> do_predicates([N#{pt => P} || N <- At], Preds); attribute_node(#{nk := _}, _) -> []. %% ================================ %% child - forward %% ================================ -spec child_comment(document() | element(), step()) -> [comment()]. child_comment( #{ id := ?DB_ND, nk := Nk } = P, {Preds} ) when Nk =:= element; Nk =:= document -> Cn = simple_path(P, [{child, comment}]), do_predicates(Cn, Preds); child_comment( #{ nk := Nk, ch := _ } = P, {Preds} ) when Nk =:= element; Nk =:= document -> Cn = [N || #{nk := comment} = N <- xqldb_mem_nodes:children(P)], do_predicates(Cn, Preds); child_comment(#{nk := _}, _) -> []. -spec child_element(document() | element(), step()) -> [element()]. child_element( #{ id := ?DB_ND, nk := Nk } = P, {{A, B, _Type}, Preds} ) when Nk =:= element; Nk =:= document -> Cn = simple_path(P, [{child, {A, B}}]), do_predicates(Cn, Preds); child_element( #{ nk := Nk, ch := _ } = P, {NameAndType, Preds} ) when Nk =:= element; Nk =:= document -> Cn = [ N || #{ nk := element, nn := NodeName, tn := TypeName } = N <- xqldb_mem_nodes:children(P), name_type_match(NameAndType, NodeName, TypeName) ], do_predicates(Cn, Preds); child_element(#{nk := _}, _) -> []. -spec child_node(document() | element(), step()) -> [anychild()]. child_node( #{ id := ?DB_ND, nk := Nk } = P, {Preds} ) when Nk == element; Nk == document -> do_predicates(simple_path(P, [{child, '_'}]), Preds); child_node(#{nk := Nk} = P, {Preds}) when Nk == element; Nk == document -> do_predicates([N || N <- xqldb_mem_nodes:children(P)], Preds); child_node(#{nk := _}, _) -> []. -spec child_processing_instruction(document() | element(), step()) -> [proc_inst()]. child_processing_instruction( #{ id := ?DB_ND, nk := Nk } = P, {{Name}, Preds} ) when Nk =:= element; Nk =:= document -> Cn = simple_path(P, [{child, {pi, Name}}]), do_predicates(Cn, Preds); child_processing_instruction( #{ nk := Nk, ch := _ } = P, {Name, Preds} ) when Nk =:= element; Nk =:= document -> Cn = [ N || #{ nk := 'processing-instruction', nn := NodeName } = N <- xqldb_mem_nodes:children(P), name_match(Name, NodeName) ], do_predicates(Cn, Preds); child_processing_instruction(#{nk := _}, _) -> []. -spec child_text(document() | element(), step()) -> [text()]. child_text( #{ id := ?DB_ND, nk := Nk } = P, {Preds} ) when Nk =:= element; Nk =:= document -> Cn = simple_path(P, [{child, text}]), do_predicates(Cn, Preds); child_text( #{ nk := Nk, ch := _ } = P, {Preds} ) when Nk =:= element; Nk =:= document -> Cn = [N || #{nk := text} = N <- xqldb_mem_nodes:children(P)], do_predicates(Cn, Preds); child_text(#{nk := _}, _) -> []. %% ================================ %% descendant - forward %% ================================ -spec descendant_comment(document() | element(), step()) -> [comment()]. descendant_comment( #{ id := ?DB_ND, nk := Nk } = P, {Preds} ) when Nk =:= element; Nk =:= document -> Ds = simple_path(P, [{descendant, comment}]), do_predicates(Ds, Preds); descendant_comment(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= document -> Ds = [N || #{nk := comment} = N <- xqldb_mem_nodes:descendants(Node)], do_predicates(Ds, Preds); descendant_comment(#{nk := _}, _) -> []. -spec descendant_element(document() | element(), step()) -> [element()]. descendant_element( #{ id := ?DB_ND, nk := Nk } = P, {{A, B, _}, Preds} ) when Nk =:= element; Nk =:= document -> Ds = simple_descendant_path(A, B, P), do_predicates(Ds, Preds); descendant_element(#{nk := Nk} = Node, {NameAndType, Preds}) when Nk =:= element; Nk =:= document -> Ds = [ N || #{ nk := element, nn := NodeName, tn := TypeName } = N <- xqldb_mem_nodes:descendants(Node), name_type_match(NameAndType, NodeName, TypeName) ], do_predicates(Ds, Preds); descendant_element(#{nk := _}, _) -> []. -spec descendant_node(document() | element(), step()) -> [anychild()]. descendant_node( #{ id := ?DB_ND, nk := Nk } = P, {Preds} ) when Nk =:= element; Nk =:= document -> do_predicates(simple_path(P, [{descendant, '_'}]), Preds); descendant_node(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= document -> do_predicates(xqldb_mem_nodes:descendants(Node), Preds); descendant_node(#{nk := _}, _) -> []. -spec descendant_processing_instruction(document() | element(), step()) -> [proc_inst()]. descendant_processing_instruction( #{ id := ?DB_ND, nk := Nk } = P, {{Name}, Preds} ) when Nk =:= element; Nk =:= document -> Ds = simple_path(P, [{descendant, {pi, Name}}]), do_predicates(Ds, Preds); descendant_processing_instruction(#{nk := Nk} = Node, {Name, Preds}) when Nk =:= element; Nk =:= document -> Ds = [ N || #{ nk := 'processing-instruction', nn := NodeName } = N <- xqldb_mem_nodes:descendants(Node), name_match(Name, NodeName) ], do_predicates(Ds, Preds); descendant_processing_instruction(#{nk := _}, _) -> []. -spec descendant_text(document() | element(), step()) -> [text()]. descendant_text( #{ id := ?DB_ND, nk := Nk } = P, {Preds} ) when Nk =:= element; Nk =:= document -> Ds = simple_path(P, [{descendant, text}]), do_predicates(Ds, Preds); descendant_text(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= document -> Ds = [N || #{nk := text} = N <- xqldb_mem_nodes:descendants(Node)], do_predicates(Ds, Preds); descendant_text(#{nk := _}, _) -> []. %% ========================== %% descendant_or_self - forward %% ========================== -spec descendant_or_self_attribute(attribute(), step()) -> [attribute()]. descendant_or_self_attribute(#{nk := attribute} = Node, Step) -> self_attribute(Node, Step); descendant_or_self_attribute(#{nk := _}, _) -> []. -spec descendant_or_self_comment(comment() | document() | element(), step()) -> [comment()]. descendant_or_self_comment(#{nk := comment} = Node, Step) -> self_comment(Node, Step); descendant_or_self_comment(Node, {Preds}) -> descendant_comment(Node, {Preds}). -spec descendant_or_self_document_node(document(), step()) -> [document()]. descendant_or_self_document_node(#{nk := document} = Node, Step) -> self_document_node(Node, Step); descendant_or_self_document_node(#{nk := _}, _) -> []. -spec descendant_or_self_element(document() | element(), step()) -> [element()]. descendant_or_self_element( #{ id := ?DB_ND, nk := document } = P, {{A, B, _}, Preds} ) -> Ds = simple_descendant_path(A, B, P), do_predicates(Ds, Preds); descendant_or_self_element( #{ id := ?DB_ND, nk := element, nn := Nn } = P, {{A, B, _} = Nt, Preds} ) -> Ds = case name_type_match(Nt, Nn, '_') of true -> [P | simple_path(P, [{descendant, {A, B}}])]; false -> simple_path(P, [{descendant, {A, B}}]) end, do_predicates(Ds, Preds); descendant_or_self_element(#{nk := Nk} = Node, {NameAndType, Preds}) when Nk =:= element; Nk =:= document -> Ds = [ D || #{ nk := element, nn := NodeName, tn := TypeName } = D <- [Node | xqldb_mem_nodes:descendants(Node)], name_type_match(NameAndType, NodeName, TypeName) ], do_predicates(Ds, Preds); descendant_or_self_element(#{nk := _}, _) -> []. -spec descendant_or_self_node( attribute() | comment() | document() | element() | proc_inst() | text(), step() ) -> [anynode()]. descendant_or_self_node(#{nk := Nk} = Node, Step) when Nk =:= attribute; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> self_node(Node, Step); descendant_or_self_node( #{ id := ?DB_ND, nk := Nk } = Node, {Preds} ) when Nk =:= element; Nk =:= document -> Ds = [Node | simple_path(Node, [{descendant, '_'}])], do_predicates(Ds, Preds); descendant_or_self_node(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= document -> Ds = [Node | xqldb_mem_nodes:descendants(Node)], do_predicates(Ds, Preds); descendant_or_self_node(#{nk := _}, _) -> []. -spec descendant_or_self_processing_instruction(document() | element() | proc_inst(), step()) -> [proc_inst()]. descendant_or_self_processing_instruction(#{nk := 'processing-instruction'} = Node, Step) -> self_processing_instruction(Node, Step); descendant_or_self_processing_instruction(Node, Step) -> descendant_processing_instruction(Node, Step). -spec descendant_or_self_text(document() | element() | text(), step()) -> [text()]. descendant_or_self_text(#{nk := text} = Node, Step) -> self_text(Node, Step); descendant_or_self_text(Node, Step) -> descendant_text(Node, Step). %% ========================== %% following - forward %% ========================== -spec following_comment(attribute() | comment() | element() | proc_inst() | text(), step()) -> [comment()]. following_comment( #{ id := ?DB_ND, nk := Nk } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = [P || #{nk := comment} = P <- xqldb_nodes:following(Node)], do_predicates(Fol, Preds); following_comment(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = [P || #{nk := comment} = P <- following(Node)], do_predicates(Fol, Preds); following_comment(#{nk := _}, _) -> []. -spec following_element(attribute() | comment() | element() | proc_inst() | text(), step()) -> [element()]. following_element( #{ id := ?DB_ND, nk := Nk } = Node, {NameAndType, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = [ P || #{ nk := element, nn := NodeName } = P <- xqldb_nodes:following(Node), name_type_match(NameAndType, NodeName, '_') ], do_predicates(Fol, Preds); following_element(#{nk := Nk} = Node, {NameAndType, Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = [ P || #{ nk := element, nn := NodeName, tn := TypeName } = P <- following(Node), name_type_match(NameAndType, NodeName, TypeName) ], do_predicates(Fol, Preds); following_element(#{nk := _}, _) -> []. -spec following_node(attribute() | comment() | element() | proc_inst() | text(), step()) -> [anychild()]. following_node( #{ id := ?DB_ND, nk := Nk } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = xqldb_nodes:following(Node), do_predicates(Fol, Preds); following_node(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = following(Node), do_predicates(Fol, Preds); following_node(#{nk := _}, _) -> []. -spec following_processing_instruction( attribute() | comment() | element() | proc_inst() | text(), step() ) -> [proc_inst()]. following_processing_instruction( #{ id := ?DB_ND, nk := Nk } = Node, {Name, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = [ P || #{ nk := 'processing-instruction', nn := NodeName } = P <- xqldb_nodes:following(Node), name_match(Name, NodeName) ], do_predicates(Fol, Preds); following_processing_instruction(#{nk := Nk} = Node, {Name, Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = [ P || #{ nk := 'processing-instruction', nn := NodeName } = P <- following(Node), name_match(Name, NodeName) ], do_predicates(Fol, Preds); following_processing_instruction(#{nk := _}, _) -> []. -spec following_text(attribute() | comment() | element() | proc_inst() | text(), step()) -> [text()]. following_text( #{ id := ?DB_ND, nk := Nk } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = [P || #{nk := text} = P <- xqldb_nodes:following(Node)], do_predicates(Fol, Preds); following_text(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Fol = [P || #{nk := text} = P <- following(Node)], do_predicates(Fol, Preds); following_text(#{nk := _}, _) -> []. %% ========================== %% following_sibling - forward %% ========================== -spec following_sibling_comment(comment() | element() | proc_inst() | text(), step()) -> [comment()]. following_sibling_comment( #{ nk := Nk, id := ?DB_ND = Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{ id := SId, nk := comment } = S <- xqldb_nodes:siblings(Node), SId > Id ], do_predicates(Sibs, Preds); following_sibling_comment( #{ nk := Nk, id := Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{ id := SId, nk := comment } = S <- siblings(Node), SId > Id ], do_predicates(Sibs, Preds); following_sibling_comment(#{nk := _}, _) -> []. -spec following_sibling_element(comment() | element() | proc_inst() | text(), step()) -> [element()]. following_sibling_element( #{ nk := Nk, id := ?DB_ND = _Id } = Node, {{A, B, _}, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = simple_path(Node, [{'following-sibling', {A, B}}]), do_predicates(Sibs, Preds); following_sibling_element( #{ nk := Nk, id := Id } = Node, {NameAndType, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{ id := SId, nk := element, nn := NodeName, tn := TypeName } = S <- siblings(Node), SId > Id, name_type_match(NameAndType, NodeName, TypeName) ], do_predicates(Sibs, Preds); following_sibling_element(#{nk := _}, _) -> []. -spec following_sibling_node(comment() | element() | proc_inst() | text(), step()) -> [anychild()]. following_sibling_node( #{ nk := Nk, id := ?DB_ND = Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{id := SId} = S <- xqldb_nodes:siblings(Node), SId > Id ], do_predicates(Sibs, Preds); following_sibling_node( #{ nk := Nk, id := Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{id := SId} = S <- siblings(Node), SId > Id ], do_predicates(Sibs, Preds); following_sibling_node(#{nk := _}, _) -> []. -spec following_sibling_processing_instruction( comment() | element() | proc_inst() | text(), step() ) -> [proc_inst()]. following_sibling_processing_instruction( #{ nk := Nk, id := ?DB_ND = Id } = Node, {Name, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{ id := SId, nk := 'processing-instruction', nn := NodeName } = S <- xqldb_nodes:siblings(Node), SId > Id, name_match(Name, NodeName) ], do_predicates(Sibs, Preds); following_sibling_processing_instruction( #{ nk := Nk, id := Id } = Node, {Name, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{ id := SId, nk := 'processing-instruction', nn := NodeName } = S <- siblings(Node), SId > Id, name_match(Name, NodeName) ], do_predicates(Sibs, Preds); following_sibling_processing_instruction(#{nk := _}, _) -> []. -spec following_sibling_text(comment() | element() | proc_inst() | text(), step()) -> [text()]. following_sibling_text( #{ nk := Nk, id := ?DB_ND = Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{ id := SId, nk := text } = S <- xqldb_nodes:siblings(Node), SId > Id ], do_predicates(Sibs, Preds); following_sibling_text( #{ nk := Nk, id := Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = [ S || #{ id := SId, nk := text } = S <- siblings(Node), SId > Id ], do_predicates(Sibs, Preds); following_sibling_text(#{nk := _}, _) -> []. %% ========================== %% parent - reverse %% ========================== -spec parent_document_node(comment() | element() | proc_inst() | text(), step()) -> [document()]. parent_document_node( #{ id := {_, _, <<_:4/binary>>}, nk := Nk } = Node, Step ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> case xqldb_nodes:parent(Node) of [] -> []; Par -> self_document_node(Par, Step) end; parent_document_node( #{ id := ?DB_ND, nk := _ }, _ ) -> []; parent_document_node(#{nk := Nk} = Node, Step) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> case xqldb_mem_nodes:parent(Node) of [] -> []; Par -> self_document_node(Par, Step) end; parent_document_node(#{nk := _}, _) -> []. -spec parent_element(attribute() | comment() | element() | proc_inst() | text(), step()) -> [element()]. parent_element( #{ id := ?DB_ND, nk := Nk } = Node, Step ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> case xqldb_nodes:parent(Node) of [] -> []; Par -> self_element(Par, Step) end; parent_element(#{nk := Nk} = Node, Step) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> case xqldb_mem_nodes:parent(Node) of [] -> []; Par -> self_element(Par, Step) end; parent_element(#{nk := _}, _) -> []. -spec parent_node(attribute() | comment() | element() | proc_inst() | text(), step()) -> [document() | element()]. parent_node( #{ id := ?DB_ND, nk := Nk } = Node, Step ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> case xqldb_nodes:parent(Node) of [] -> []; Par -> self_node(Par, Step) end; parent_node(#{nk := Nk} = Node, Step) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> case xqldb_mem_nodes:parent(Node) of [] -> []; Par -> self_node(Par, Step) end; parent_node(#{nk := _}, _) -> []. %% ========================== %% preceding - reverse %% ========================== -spec preceding_comment(attribute() | comment() | element() | proc_inst() | text(), step()) -> [comment()]. preceding_comment( #{ id := ?DB_ND, nk := Nk } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = [P || #{nk := comment} = P <- xqldb_nodes:preceding(Node)], Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_comment(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = [P || #{nk := comment} = P <- preceding(Node)], Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_comment(#{nk := _}, _) -> []. -spec preceding_element(attribute() | comment() | element() | proc_inst() | text(), step()) -> [element()]. preceding_element( #{ id := ?DB_ND, nk := Nk } = Node, {NameAndType, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = [ P || #{ nk := element, nn := NodeName } = P <- xqldb_nodes:preceding(Node), name_type_match(NameAndType, NodeName, '_') ], Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_element(#{nk := Nk} = Node, {NameAndType, Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = [ P || #{ nk := element, nn := NodeName, tn := TypeName } = P <- preceding(Node), name_type_match(NameAndType, NodeName, TypeName) ], Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_element(#{nk := _}, _) -> []. -spec preceding_node(attribute() | comment() | element() | proc_inst() | text(), step()) -> [anychild()]. preceding_node( #{ id := ?DB_ND, nk := Nk } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = xqldb_nodes:preceding(Node), Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_node(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = preceding(Node), Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_node(#{nk := _}, _) -> []. -spec preceding_processing_instruction( attribute() | comment() | element() | proc_inst() | text(), step() ) -> [proc_inst()]. preceding_processing_instruction( #{ id := ?DB_ND, nk := Nk } = Node, {Name, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = [ P || #{ nk := 'processing-instruction', nn := NodeName } = P <- xqldb_nodes:preceding(Node), name_match(Name, NodeName) ], Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_processing_instruction(#{nk := Nk} = Node, {Name, Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = [ P || #{ nk := 'processing-instruction', nn := NodeName } = P <- preceding(Node), name_match(Name, NodeName) ], Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_processing_instruction(#{nk := _}, _) -> []. -spec preceding_text(attribute() | comment() | element() | proc_inst() | text(), step()) -> [text()]. preceding_text( #{ id := ?DB_ND, nk := Nk } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = [P || #{nk := text} = P <- xqldb_nodes:preceding(Node)], Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_text(#{nk := Nk} = Node, {Preds}) when Nk =:= element; Nk =:= text; Nk =:= attribute; Nk =:= comment; Nk =:= 'processing-instruction' -> Pre = [P || #{nk := text} = P <- preceding(Node)], Fil = do_predicates(Pre, Preds), lists:reverse(Fil); preceding_text(#{nk := _}, _) -> []. %% ========================== %% preceding_sibling - reverse %% ========================== -spec preceding_sibling_comment(comment() | element() | proc_inst() | text(), step()) -> [comment()]. preceding_sibling_comment( #{ nk := Nk, id := Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = siblings(Node), Sibs1 = [ S || #{ id := SId, nk := comment } = S <- Sibs, SId < Id ], Sibs2 = lists:reverse(Sibs1), Sibs3 = do_predicates(Sibs2, Preds), lists:reverse(Sibs3); preceding_sibling_comment(#{nk := _}, _) -> []. -spec preceding_sibling_element(comment() | element() | proc_inst() | text(), step()) -> [element()]. preceding_sibling_element( #{ nk := Nk, id := ?DB_ND = Id } = Node, {NameAndType, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = xqldb_nodes:siblings(Node), Sibs1 = [ S || #{ id := SId, nk := element, nn := NodeName } = S <- Sibs, name_type_match(NameAndType, NodeName, '_'), SId < Id ], Sibs2 = lists:reverse(Sibs1), Sibs3 = do_predicates(Sibs2, Preds), lists:reverse(Sibs3); preceding_sibling_element( #{ nk := Nk, id := Id } = Node, {NameAndType, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = siblings(Node), Sibs1 = [ S || #{ id := SId, nk := element, nn := NodeName, tn := TypeName } = S <- Sibs, name_type_match(NameAndType, NodeName, TypeName), SId < Id ], Sibs2 = lists:reverse(Sibs1), Sibs3 = do_predicates(Sibs2, Preds), lists:reverse(Sibs3); preceding_sibling_element(#{nk := _}, _) -> []. -spec preceding_sibling_node(comment() | element() | proc_inst() | text(), step()) -> [anychild()]. preceding_sibling_node( #{ nk := Nk, id := Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = siblings(Node), Sibs1 = [S || #{id := SId} = S <- Sibs, SId < Id], Sibs2 = lists:reverse(Sibs1), Sibs3 = do_predicates(Sibs2, Preds), lists:reverse(Sibs3); preceding_sibling_node(#{nk := _}, _) -> []. -spec preceding_sibling_processing_instruction( comment() | element() | proc_inst() | text(), step() ) -> [proc_inst()]. preceding_sibling_processing_instruction( #{ nk := Nk, id := Id } = Node, {Name, Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = siblings(Node), Sibs1 = [ S || #{ id := SId, nk := 'processing-instruction', nn := NodeName } = S <- Sibs, name_match(Name, NodeName), SId < Id ], Sibs2 = lists:reverse(Sibs1), Sibs3 = do_predicates(Sibs2, Preds), lists:reverse(Sibs3); preceding_sibling_processing_instruction(#{nk := _}, _) -> []. -spec preceding_sibling_text(comment() | element() | proc_inst() | text(), step()) -> [text()]. preceding_sibling_text( #{ nk := Nk, id := Id } = Node, {Preds} ) when Nk =:= element; Nk =:= text; Nk =:= comment; Nk =:= 'processing-instruction' -> Sibs = siblings(Node), Sibs1 = [ S || #{ id := SId, nk := text } = S <- Sibs, SId < Id ], Sibs2 = lists:reverse(Sibs1), Sibs3 = do_predicates(Sibs2, Preds), lists:reverse(Sibs3); preceding_sibling_text(#{nk := _}, _) -> []. do_predicates_if_matched(NameAndType, NodeName, TypeName, Node, Preds) -> case name_type_match(NameAndType, NodeName, TypeName) of true -> do_predicates([Node], Preds); false -> [] end. %% ========================== %% self - forward %% ========================== -spec self_attribute(attribute(), step()) -> [attribute()]. self_attribute( #{ nk := attribute, nn := NodeName, tn := TypeName } = Node, {NameAndType, Preds} ) -> do_predicates_if_matched(NameAndType, NodeName, TypeName, Node, Preds); self_attribute(#{nk := _}, _) -> []. -spec self_comment(comment(), step()) -> [comment()]. self_comment(#{nk := comment} = Node, {Preds}) -> do_predicates([Node], Preds); self_comment(#{nk := _}, _) -> []. -spec self_document_node(document(), step()) -> [document()]. self_document_node( #{ nk := document, id := ?DB_ND } = Node, {NameAndType, Preds} ) -> case child_element(Node, {{'_', '_', '_'}, []}) of [#{nn := NodeName}] -> do_predicates_if_matched(NameAndType, NodeName, '_', Node, Preds); _ -> [] end; self_document_node( #{ nk := document, ch := Ch } = Node, {NameAndType, Preds} ) -> case child_element(Node, {{'_', '_', '_'}, []}) of [#{nn := NodeName, tn := TypeName}] -> do_predicates_if_matched(NameAndType, NodeName, TypeName, Node, Preds); [] when Ch == [] -> do_predicates([Node], Preds); _ -> [] end; self_document_node(#{nk := document} = Node, {Preds}) -> do_predicates([Node], Preds); self_document_node(#{nk := _}, _) -> []. -spec self_element(element(), step()) -> [element()]. self_element( #{ id := ?DB_ND, nk := element, nn := NodeName } = Node, {NameAndType, Preds} ) -> do_predicates_if_matched(NameAndType, NodeName, '_', Node, Preds); self_element( #{ nk := element, nn := NodeName, tn := TypeName } = Node, {NameAndType, Preds} ) -> do_predicates_if_matched(NameAndType, NodeName, TypeName, Node, Preds); self_element(#{nk := _}, _) -> []. -spec self_namespace(namespace(), step()) -> [namespace()]. self_namespace(#{nk := namespace} = Node, {Preds}) -> do_predicates([Node], Preds); self_namespace(#{nk := _}, _) -> []. -spec self_node(attribute() | comment() | document() | element() | proc_inst() | text(), step()) -> [anynode()]. self_node(Node, {Preds}) -> do_predicates([Node], Preds). -spec self_processing_instruction(proc_inst(), step()) -> [proc_inst()]. self_processing_instruction( #{ nk := 'processing-instruction', nn := NodeName } = Node, {Name, Preds} ) -> case name_match(Name, NodeName) of true -> do_predicates([Node], Preds); false -> [] end; self_processing_instruction(#{nk := _}, _) -> []. -spec self_text(text(), step()) -> [text()]. self_text(#{nk := text} = Node, {Preds}) -> do_predicates([Node], Preds); self_text(#{nk := _}, _) -> []. document_order([_] = NodeList) -> NodeList; document_order([]) -> []; document_order(NodeList) when is_list(NodeList) -> case catch (doc_ord(lists:flatten(NodeList))) of {'EXIT', _} -> {error, non_node}; O -> O end; document_order(Node) -> [Node]. %% ========================================================================== %% Internal functions %% ========================================================================== doc_ord(NodeList) -> lists:usort(fun doc_ord_fun/2, NodeList). doc_ord_fun(#{id := A}, #{id := B}) -> A =< B. doc_rev_ord(NodeList) -> lists:usort(fun doc_rev_ord_fun/2, NodeList). doc_rev_ord_fun(#{id := A}, #{id := B}) -> A > B. siblings(#{id := ?DB_ND} = Node) -> xqldb_nodes:siblings(Node); siblings(Node) -> case xqldb_mem_nodes:parent(Node) of #{ch := _} = P -> xqldb_mem_nodes:children(P); _ -> [] end. preceding(Node) -> Par = xqldb_mem_nodes:parent(Node), case Par of [] -> []; _ -> Acc = [ N || S <- preceding_sibling_node(Node, {[]}), N <- descendant_or_self_node(S, {[]}) ], All = preceding(Par, Acc), doc_rev_ord(All) end. preceding(Node, Acc) -> case xqldb_mem_nodes:parent(Node) of [] -> Acc; Par -> Nds = [ N || S <- preceding_sibling_node(Node, {[]}), N <- descendant_or_self_node(S, {[]}) ], preceding(Par, Nds ++ Acc) end. following(Node) -> Par = xqldb_mem_nodes:parent(Node), case Par of [] -> []; _ -> Acc = [ N || S <- following_sibling_node(Node, {[]}), N <- descendant_or_self_node(S, {[]}) ], following(Par, Acc) end. following(Node, Acc) -> case xqldb_mem_nodes:parent(Node) of [] -> Acc; Par -> Nds = [ N || S <- following_sibling_node(Node, {[]}), N <- descendant_or_self_node(S, {[]}) ], following(Par, Acc ++ Nds) end. type_match('_', _) -> true; type_match(Ty, Ty) -> true; type_match(_, _) -> false. name_type_match({'_', '_', '_'}, _, _) -> true; name_type_match({'_', '_', Ty}, {_, _, _}, Ty) -> true; name_type_match({'_', Ln, '_'}, {_, _, Ln}, _) -> true; name_type_match({'_', Ln, Ty}, {_, _, Ln}, Ty) -> true; name_type_match({Ns, '_', '_'}, {Ns, _, _}, _) -> true; name_type_match({Ns, '_', Ty}, {Ns, _, _}, Ty) -> true; name_type_match({Ns, Ln, '_'}, {Ns, _, Ln}, _) -> true; name_type_match({Ns, Ln, Ty}, {Ns, _, Ln}, Ty) -> true; name_type_match(_, _, _) -> false. name_match({'_'}, _) -> true; name_match({Ln}, {_, _, Ln}) -> true; name_match(_, _) -> false. do_predicates(Nodes, []) -> Nodes; do_predicates(Nodes, [P | Ps]) -> S = fun() -> length(Nodes) end, F = do_pred_filter(P, Nodes, 1, S), do_predicates(F, Ps). do_pred_filter(Pred, [Node | Nodes], Pos, Size) -> case Pred(Node, Pos, Size) of true -> [Node | do_pred_filter(Pred, Nodes, Pos + 1, Size)]; false -> do_pred_filter(Pred, Nodes, Pos + 1, Size) end; do_pred_filter(_, [], _, _) -> []. %% add_pos([H|T], Pos) -> %% [{H,Pos}|add_pos(T, Pos + 1)]; %% add_pos([], _) -> []. %% at = attributes %% bu = base-uri (list of all xml:base atts to document) %% ch = children %% du = document-uri %% id = node-identity (unique ID in document) %% ii = is-id %% ir = is-idrefs %% ns = namespace-nodes (in-scope namespaces) %% nd = nilled %% nk = node-kind %% nn = node-name %% pt = parent %% sv = string-value %% tn = type-name %% tv = typed-value build_mem_node_fun(Steps) -> try get_funs_from_steps(Steps) of [{H, P} | T] -> fun(Node) -> All = [N || X <- H(Node, P), N <- build_mem_node_fun(X, T)], document_order(lists:flatten(All)) end catch _:_ -> fun(_) -> [] end end. % out of steps return node build_mem_node_fun(Node, []) -> [Node]; %build_mem_node_fun(Node, [F]) when is_function(F) -> [F(Node)]; % out of steps return node build_mem_node_fun(Node, [{H, P} | Steps]) -> [ N || X <- H(Node, P), N <- build_mem_node_fun(X, Steps) ]. get_funs_from_steps([]) -> []; get_funs_from_steps([atomize]) -> []; get_funs_from_steps([double]) -> []; get_funs_from_steps([{attribute, {att, '_', '_'}} | T]) -> [{fun attribute_node/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{attribute, {att, N, L}} | T]) -> [{fun attribute_attribute/2, {{N, L, '_'}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{child, comment} | T]) -> [{fun child_comment/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{child, {pi, L}} | T]) -> [{fun child_processing_instruction/2, {{L}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{child, {N, L}} | T]) -> [{fun child_element/2, {{N, L, '_'}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{child, '_'} | T]) -> [{fun child_node/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{child, text} | T]) -> [{fun child_text/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{descendant, comment} | T]) -> [{fun descendant_comment/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{descendant, text} | T]) -> [{fun descendant_text/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{descendant, {pi, L}} | T]) -> [{fun descendant_processing_instruction/2, {{L}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{descendant, {N, L}} | T]) -> [{fun descendant_element/2, {{N, L, '_'}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{descendant, '_'} | T]) -> [{fun descendant_node/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{'descendant-or-self', {att, N, L}} | T]) -> [{fun descendant_or_self_attribute/2, {{N, L, '_'}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{'descendant-or-self', []} | T]) -> [{fun descendant_or_self_document_node/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{'descendant-or-self', comment} | T]) -> [{fun descendant_or_self_comment/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{'descendant-or-self', text} | T]) -> [{fun descendant_or_self_text/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{'descendant-or-self', {pi, L}} | T]) -> [{fun descendant_or_self_processing_instruction/2, {{L}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{'descendant-or-self', {N, L}} | T]) -> [{fun descendant_or_self_element/2, {{N, L, '_'}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{'descendant-or-self', '_'} | T]) -> [{fun descendant_or_self_node/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{self, {att, N, L}} | T]) -> [{fun self_attribute/2, {{N, L, '_'}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{self, []} | T]) -> [{fun self_document_node/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{self, comment} | T]) -> [{fun self_comment/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{self, text} | T]) -> [{fun self_text/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{self, namespace} | T]) -> [{fun self_namespace/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps([{self, {pi, L}} | T]) -> [{fun self_processing_instruction/2, {{L}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{self, {N, L}} | T]) -> [{fun self_element/2, {{N, L, '_'}, []}} | get_funs_from_steps(T)]; get_funs_from_steps([{self, '_'} | T]) -> [{fun self_node/2, {[]}} | get_funs_from_steps(T)]; get_funs_from_steps(_) -> throw({error, unknown}).