%% ------------------------------------------------------------------- %% %% xqerl - XQuery processor %% %% Copyright (c) 2017-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. %% %% ------------------------------------------------------------------- %% @doc Processing for local nodes created in XQuery. Translation from %% 'doc' -> node. %% TODO : Complete rewrite. simplify -module(xqerl_node). -compile(inline_list_funcs). -include("xqerl.hrl"). -define(UNTYP(Val), #xqAtomicValue{type = 'xs:untypedAtomic', value = Val}). % -define(STR_REST(Str, Rest), <>). % -define(CP_REST(Cp, Rest), <>). % -define(SSTR(S), <>). % block array:array(_) warnings -dialyzer(no_opaque). -define(IS_ARRAY(A), is_tuple(A), element(1, A) =:= array). -define(XML, <<"http://www.w3.org/XML/1998/namespace">>). -define(XMLNS, <<"http://www.w3.org/2000/xmlns/">>). -export([ copy_node/1, contruct/2, expand_node_name/2, expand_attribute_name/2, expand_pi_name/1, expand_ns_name/1 ]). -export([atomize_nodes/1, atomize_node/2]). -export([to_xml/1]). -export([nodes_equal/3]). %node_to_content/2, -export([ ensure_qname/2 ]). -export([get_node_type/1]). % for fn:generate-id -export([get_node_hash/1]). -export([merge_content/2]). expand_node_name(Ctx, [V]) -> expand_node_name(Ctx, V); expand_node_name(Ctx, #xqAtomicValue{ type = 'xs:untypedAtomic', value = Val }) -> expand_node_name(Ctx, Val); expand_node_name(_Ctx, []) -> ?err('XPTY0004'); expand_node_name(_Ctx, V) when is_number(V); is_atom(V) -> ?err('XPTY0004'); expand_node_name(_Ctx, #qname{ namespace = N, prefix = P, local_name = L }) -> {N, P, L}; expand_node_name(_Ctx, #xqAtomicValue{ value = #qname{ namespace = N, prefix = P, local_name = L } }) -> {N, P, L}; expand_node_name(_Ctx, #xqAtomicValue{}) -> ?err('XPTY0004'); expand_node_name(#{namespaces := Nss} = Ctx, Expr) -> % can throw XPTY0004 case xqerl_types:atomize(Expr) of [_, _ | _] -> ?dbg("Atom", Expr), ?err('XPTY0004'); Atom -> %?dbg("Atom", Atom), try QName = xqerl_types:cast_as(Atom, 'xs:QName', Nss), expand_node_name(Ctx, QName) catch _:_ -> ?err('XQDY0074') end end. expand_attribute_name(Ctx, [V]) -> expand_attribute_name(Ctx, V); expand_attribute_name(Ctx, #xqAtomicValue{ type = 'xs:untypedAtomic', value = Val }) -> expand_attribute_name(Ctx, Val); expand_attribute_name(_Ctx, []) -> ?err('XPTY0004'); expand_attribute_name(_Ctx, V) when is_number(V); is_atom(V) -> ?err('XPTY0004'); expand_attribute_name(_Ctx, #qname{ namespace = N, prefix = P, local_name = L }) -> {N, P, L}; expand_attribute_name(_Ctx, #xqAtomicValue{ value = #qname{ namespace = N, prefix = P, local_name = L } }) -> {N, P, L}; expand_attribute_name(_Ctx, #xqAtomicValue{}) -> ?err('XPTY0004'); expand_attribute_name(#{namespaces := Nss} = Ctx, Expr) -> % can throw XPTY0004 case xqerl_types:atomize(Expr) of [_, _ | _] -> ?dbg("Atom", Expr), ?err('XPTY0004'); Atom -> %?dbg("Atom", Atom), try xqerl_types:cast_as(Atom, 'xs:QName', Nss) of #xqAtomicValue{ value = #qname{ namespace = _, prefix = <<>>, local_name = L } } -> {<<>>, <<>>, L}; QName -> expand_attribute_name(Ctx, QName) catch _:_ -> ?err('XQDY0074') end end. expand_pi_name(#xqAtomicValue{ type = 'xs:untypedAtomic', value = Val }) -> expand_pi_name(Val); expand_pi_name(#qname{local_name = L}) -> {<<>>, <<>>, L}; expand_pi_name(#xqAtomicValue{value = #qname{local_name = L}}) -> {<<>>, <<>>, L}; expand_pi_name(Atom) when is_binary(Atom) -> try #xqAtomicValue{value = L} = xqerl_types:cast_as(Atom, 'xs:NCName'), {<<>>, <<>>, L} catch _:_ -> ?err('XQDY0041') end; expand_pi_name(L) when is_list(L) -> case lists:flatten(L) of [X] -> expand_pi_name(X); _ -> ?err('XPTY0004') end; expand_pi_name(_) -> ?err('XPTY0004'). expand_ns_name({[U], P}) -> expand_ns_name({U, P}); expand_ns_name({U, [P]}) -> expand_ns_name({U, P}); expand_ns_name({U, []}) -> U1 = xqerl_types:cast_as(U, 'xs:string'), {U1, <<>>}; expand_ns_name({U, <<>>}) -> U1 = xqerl_types:cast_as(U, 'xs:string'), {U1, <<>>}; expand_ns_name({U, #xqAtomicValue{type = 'xs:untypedAtomic', value = Val}}) -> expand_ns_name({U, Val}); expand_ns_name({U, P}) when is_binary(P) -> try #xqAtomicValue{value = U1} = xqerl_types:cast_as(U, 'xs:anyURI'), #xqAtomicValue{value = P1} = xqerl_types:cast_as(P, 'xs:NCName'), {U1, P1} catch _:_ -> ?dbg("P", P), ?err('XQDY0074') end; expand_ns_name({_, _}) -> ?err('XPTY0004'). contruct(Ctx, [Node]) -> contruct(Ctx, Node); contruct(Ctx, #{id := {_, _, _}} = Node) -> contruct(Ctx, copy_node(Node)); contruct( #{ 'base-uri' := BaseUri, namespaces := _Nss } = Ctx, #{ id := {Ref, _}, nk := Nk } = Content ) -> BaseUri1 = case BaseUri of #xqAtomicValue{value = V} -> V; V when is_binary(V) -> V end, %Default = get_default_namespace(Nss), %, InScopeNs = maps:get(inscope_ns, Ctx, #{ <<"xml">> => ?XML %<<>> => Default }), Ctx1 = case Nk of document -> new_doc_context(Ctx); _ -> new_context(Ctx) end, Ctx2 = Ctx1#{ inscope_ns => InScopeNs, 'base-uri' := BaseUri1 }, case is_updating_context(Ctx2) of true -> Ref1 = make_ref(), {Node, _Ctx} = contruct_node(Ctx2, Content, Ref1, InScopeNs), Node; false -> {Node, _Ctx} = contruct_node(Ctx2, Content, Ref, InScopeNs), Node end. alist(L) when is_list(L) -> L; alist(L) -> [L]. is_updating_context(#{updating := true}) -> true; is_updating_context(_) -> false. new_context(Ctx) -> Ctx#{next_node_id => 1}. new_doc_context(Ctx) -> Ctx#{next_node_id => 0}. % returns {Id, Ctx} next_id(#{next_node_id := Last} = Ctx) -> {Last, Ctx#{next_node_id := Last + 1}}. % namespaces are in the attributes when a direct cons % also sets the ids of the attribute nodes split_add_namespace_attributes(Atts, Ctx, Ref) -> F = fun (#{nk := namespace} = N, {{Ns, As}, Ctx1}) -> {{[N | Ns], As}, Ctx1}; (N, {{Ns, As}, Ctx1}) -> % N is an attribute {Id, Ctx2} = next_id(Ctx1), NewId = {Ref, Id}, {N1, _} = contruct_node(Ctx1, N, Ref, []), {{Ns, [N1#{id := NewId} | As]}, Ctx2} end, lists:foldl(F, {{[], []}, Ctx}, alist(Atts)). augment_direct_inscope_namespaces(New, Old) -> F = fun( #{ nk := namespace, nn := {Ns, Px} }, Acc ) -> Acc#{Px => Ns} end, lists:foldl(F, Old, New). augment_computed_inscope_namespaces(New, Old) -> F = fun( #{ nk := namespace, nn := {Ns, Px} }, Acc ) -> case Acc of #{Px := Ns} -> Acc; #{Px := _} -> NewPx = xqerl_lib:next_comp_prefix(Acc), %?dbg("Ns, Px, NewPx", {Ns, Px, NewPx}), Acc#{NewPx => Ns}; _ -> Acc#{Px => Ns} end end, lists:foldl(F, Old, New). augment_context_inscope_namespaces([], Ctx) -> Ctx; augment_context_inscope_namespaces( New, #{ namespaces := Nss, inscope_ns := InScopeNs } = Ctx ) -> F = fun( #{ nk := namespace, nn := {Ns, Px} }, {Acc, Acc1} ) -> NewNs = #xqNamespace{namespace = Ns, prefix = Px}, {lists:keystore(Px, 3, Acc, NewNs), Acc1#{Px => Ns}} end, {Aug, Aug1} = lists:foldl(F, {Nss, InScopeNs}, New), %?dbg("Aug",Aug), Ctx#{ namespaces := Aug, inscope_ns := Aug1 }. augment_context_base_uri([], Ctx) -> Ctx; augment_context_base_uri(New, #{'base-uri' := BaseUri} = Ctx) -> BaseUri1 = case BaseUri of #xqAtomicValue{value = V} -> V; V when is_binary(V) -> V end, try xqerl_lib:resolve_against_base_uri(BaseUri1, New) of {error, _} -> Ctx#{'base-uri' := New}; BaseUri2 -> Ctx#{'base-uri' := BaseUri2} catch _:_ -> Ctx#{'base-uri' := New} end. get_new_base_uri([]) -> []; get_new_base_uri([ #{ nk := attribute, nn := {?XML, _, <<"base">>}, sv := Sv } | _ ]) -> xqerl_types:string_value(Sv); get_new_base_uri([_ | T]) -> get_new_base_uri(T). get_curr_id(#{next_node_id := Id}) -> Id. set_curr_id(Id, Ctx) -> Ctx#{next_node_id := Id}. split_doc_content(Children) -> split_content(Children, doc, {[], [], []}). split_content(Children) -> split_content(Children, tag, {[], [], []}). % turn namespaces into {Px, Ns} split_content([], _, {Nss, Atts, Children}) -> {lists:reverse(Nss), lists:reverse(Atts), lists:reverse(Children)}; split_content([#{nk := attribute} | _], doc, _) -> ?err('XPTY0004'); split_content([#{nk := attribute} = H | T], tag, {Nss, Atts, Children}) -> split_content(T, tag, {Nss, [H | Atts], Children}); split_content([#{nk := attribute} | _], content, _) -> ?err('XQTY0024'); split_content([#{nk := namespace} | _], doc, _) -> ?err('XPTY0004'); split_content([#{nk := namespace} = H | T], tag, {Nss, Atts, Children}) -> split_content(T, tag, {[H | Nss], Atts, Children}); split_content([#{nk := namespace} | _], content, _) -> ?err('XQTY0024'); split_content([H | T], doc, {Nss, Atts, Children}) -> split_content(T, doc, {Nss, Atts, [H | Children]}); % skip empty text split_content([#{nk := text, sv := <<>>} | T], tag, Acc) -> split_content(T, tag, Acc); split_content([#{nk := text, sv := S1} = H, #{nk := text, sv := S2} | T], _, Acc) -> split_content([H#{sv := <>} | T], content, Acc); split_content([H | T], _, {Nss, Atts, Children}) -> split_content(T, content, {Nss, Atts, [H | Children]}). add_implicit_namespaces(InScopeNs, {Ns, <<>>, _}, _) -> % default namespace case InScopeNs of #{<<>> := Ns} -> {[], InScopeNs}; _ -> { [ #{ nk => namespace, nn => {Ns, <<>>} } ], InScopeNs#{<<>> => Ns} } end; add_implicit_namespaces(InScopeNs, _, _) -> {[], InScopeNs}. check_computed_namespaces([]) -> ok; %% check_computed_namespaces([{<<>>, <<>>}]) -> ok; %% check_computed_namespaces([{<<"xml">>, ?XML}]) -> ok; check_computed_namespaces(Namespaces) -> Unique = lists:usort(Namespaces), Prefixes = lists:usort([P || {P, _} <- Unique]), %?dbg("Namespaces", Namespaces), case length(Unique) == length(Prefixes) of false -> ?err('XQDY0102'); true -> %?dbg("NameSpaceNodes",NameSpaceNodes), %?dbg("NameSpaces",NameSpaces), F = fun ({<<"xml">>, Ns}) when Ns =/= ?XML -> ?err('XQDY0101'); ({Px, ?XML}) when Px =/= <<"xml">> -> ?err('XQDY0101'); ({<<"xmlns">>, _}) -> ?err('XQDY0101'); ({_, ?XMLNS}) -> ?err('XQDY0101'); ({<<>>, <<>>}) -> ok; ({_, <<>>}) -> ?err('XQDY0101'); (_) -> ok end, lists:foreach(F, Namespaces) end. is_copied_node(#{id := {Ref, _}}, Ref) -> false; is_copied_node(#{id := _}, _) -> true; is_copied_node(_, _) -> false. %{NodeName1, Atts2, InScopeNs3} % throw XUDY0024 when in an update and new prefix needed ensure_namespace_bindings(NodeName, AllAtts, InScopeNs, IsUpd) -> Fun = fun({ENs, EPx, ELn}, InScope) -> case InScope of #{EPx := ENs} -> % inscope {{ENs, EPx, ELn}, InScope}; % protect these prefixes #{EPx := _} when EPx == <<"xml">>; EPx == <<"xmlns">> -> ?err('XQDY0096'); #{EPx := _} when IsUpd == false -> % wrong prefix EPx1 = xqerl_lib:next_comp_prefix(InScope), %?dbg("{ENs, EPx1, ELn}", {ENs, EPx, EPx1, ELn}), {{ENs, EPx1, ELn}, InScope#{EPx1 => ENs}}; #{EPx := _} when IsUpd == true -> % update naming conflict ?err('XUDY0024'); _ -> {{ENs, EPx, ELn}, InScope#{EPx => ENs}} end end, Att = fun(#{nn := NN} = Node, Acc) -> case NN of {<<>>, <<>>, _} -> {Node, Acc}; _ -> {NN1, Acc1} = Fun(NN, Acc), {Node#{nn := NN1}, Acc1} end end, {NodeName1, InScopeNs1} = Fun(NodeName, InScopeNs), {Atts1, InScopeNs2} = lists:mapfoldl(Att, InScopeNs1, AllAtts), {NodeName1, Atts1, InScopeNs2}. % base-uri and inscope namespaces can be augmented by content % when a node with a different Ref is in the content, it is already built % and well-formed, just copy in with the NS preserve settings % content that is a fun/1 is from the XQuery constructor % it takes the current Ctx once namespaces are figured out % computed constructors cannot use namespaces in content contruct_node( #{'base-uri' := Bu} = Ctx, #{ nk := document, ch := ContentFun } = Node, Ref, InScopeNs ) -> % this node id {Id, Ctx1} = next_id(Ctx), NewId = {Ref, Id}, % expand content with the new context RawContent = expand_content(Ctx1, ContentFun), Content = merge_content(RawContent), {[], [], Children} = split_doc_content(Content), ChFun = fun(N, Ctx2) -> contruct_node(Ctx2, N, Ref, InScopeNs) end, {Children1, CtxOut} = lists:mapfoldl(ChFun, Ctx1, Children), Node1 = Node#{ id := NewId, bu => Bu, ch := Children1 }, {Node1, CtxOut}; contruct_node( #{'copy-namespaces' := {Preserve, Inherit}} = Ctx, #{ nk := element, nn := NodeName, at := NsAtts, ch := ContentFun } = Node, Ref, InScopeNs ) -> IsCopy = is_copied_node(Node, Ref), %?dbg("IsCopy", IsCopy), IsUpd = is_updating_context(Ctx), %?dbg("IsUpd", IsUpd), % this node id {Id, Ctx1} = next_id(Ctx), NewId = {Ref, Id}, % split direct attributes and namespaces, creates attribute nodes % Nss is list of #{nk := namespace} {{Nss, Atts}, Ctx2} = split_add_namespace_attributes(NsAtts, Ctx1, Ref), % add new namespaces to the inscope InScopeNs1 = do_namespace_augmentation(IsCopy, Node, Preserve, Inherit, IsUpd, InScopeNs, Nss), %?dbg("InScopeNs1", InScopeNs1), % get any namespaces added implicitly by the node names (not inscope!) {Nss1, InScopeNs2} = add_implicit_namespaces(InScopeNs1, NodeName, Inherit), %?dbg("InScopeNs2", InScopeNs2), %% add namespaces to context item Ctx3 = augment_context_inscope_namespaces(Nss1 ++ Nss, Ctx2), % add xml:base to context if any NewBase = get_new_base_uri(Atts), Ctx4 = augment_context_base_uri(NewBase, Ctx3), % expand content with the new context RawContent = expand_content(Ctx4, ContentFun), Content = merge_content(RawContent), % new set of namespaces, attributes and contents % these namespaces are inscope {Nss2, Atts1, Children} = split_content(Content), % check namespaces %ok = check_computed_namespaces([{P,U} || #{nn := {U,P}} <- Nss ++ Nss1 ++ Nss2]), ok = check_computed_namespaces([{P, U} || #{nn := {U, P}} <- Nss ++ Nss2]), ok = check_computed_default_override(NodeName, [{P, U} || #{nn := {U, P}} <- Nss2]), AllNss = augment_computed_inscope_namespaces(Nss2, InScopeNs2), AllAtts = Atts ++ Atts1, {NodeName1, Atts2, InScopeNs3} = ensure_namespace_bindings(NodeName, AllAtts, AllNss, IsUpd), %?dbg("InScopeNs3", InScopeNs3), ok = check_element_name(NodeName1), % add xml:base to context if any NewBase1 = get_new_base_uri(Atts1), #{'base-uri' := Bu1} = Ctx5 = augment_context_base_uri(NewBase1, Ctx4), ChFun = fun (N, Ctx6) when IsCopy -> contruct_node(Ctx6, N, Ref, InScopeNs3); % XXX if direct, then only 1 not 3 (N, Ctx6) when Inherit == inherit -> case is_copied_node(N, Ref) of true -> % try this contruct_node(Ctx6, N, Ref, InScopeNs3); %contruct_node(Ctx6, N, Ref, InScopeNs2); false -> contruct_node(Ctx6, N, Ref, InScopeNs1) end; (N, Ctx6) -> contruct_node(Ctx6, N, Ref, InScopeNs1) end, {Atts3, CurrCtx0} = lists:mapfoldl(ChFun, Ctx5, Atts2), {Children1, CurrCtx} = lists:mapfoldl(ChFun, CurrCtx0, Children), %?dbg("NodeName1", NodeName1), %?dbg("InScopeNs ", InScopeNs ), %?dbg("InScopeNs1", InScopeNs1), %?dbg("InScopeNs2", InScopeNs2), %?dbg("InScopeNs3", InScopeNs3), %?dbg("Bu", Bu), % override the id from the original context coming in. % ensure only the next id is passed on. CurrId = get_curr_id(CurrCtx), CtxOut = set_curr_id(CurrId, Ctx), Node1 = Node#{ id := NewId, nn := NodeName1, bu => Bu1, at := check_attribute_names(Atts3), ns => InScopeNs3, ch := Children1 }, %?dbg("Node1", Node1), {Node1, CtxOut}; contruct_node( Ctx, #{ nk := attribute, nn := NodeName, sv := Content } = Node, Ref, InScopeNs ) -> {Id, Ctx1} = next_id(Ctx), NewId = {Ref, Id}, %?dbg("Content", Content), Content1 = merge_text_content(Content, attribute), %?dbg("Content1", Content1), Content2 = xqerl_types:string_value(Content1), %?dbg("Content2", Content2), Node1 = case NodeName of {?XML, _, <<"id">>} -> Content3 = try xqerl_types:string_value( xqerl_types:cast_as(Content2, 'xs:ID') ) catch _:_ -> ?err('XQDY0091') end, Node#{ sv := Content3, id := NewId, tn := 'xs:ID' }; {?XML, _, <<"space">>} when Content2 == <<"preserve">>; Content2 == <<"default">> -> Node#{ id := NewId, sv := Content2 }; {?XML, _, <<"space">>} -> ?err('XQDY0092'); {Ns, <<>>, Ln} when Ns =/= <<>> -> APx = xqerl_lib:next_comp_prefix(InScopeNs), %?dbg("Ns, Px, NewPx", {Ns, <<>>, APx}), Node#{ id := NewId, nn := {Ns, APx, Ln}, sv := Content2 }; _ -> _ = check_attribute_name(Node, #{}), Node#{ id := NewId, sv := Content2 } end, {Node1, Ctx1}; contruct_node( Ctx, #{ nk := 'processing-instruction', nn := {_, _, Ln}, sv := Content } = Node, Ref, _InScopeNs ) -> case string:lowercase(Ln) of <<"xml">> -> ?err('XQDY0064'); _ -> Content1 = merge_text_content(Content, text), Content2 = xqerl_types:string_value(Content1), Content3 = string:trim(Content2, leading), case string:find(Content3, "?>") of nomatch -> {Id, Ctx1} = next_id(Ctx), NewId = {Ref, Id}, { Node#{ id => NewId, sv := Content3 }, Ctx1 }; _ -> ?err('XQDY0026') end end; contruct_node( Ctx, #{ nk := comment, sv := Content } = Node, Ref, _InScopeNs ) -> Content1 = merge_text_content(Content, text), Content2 = xqerl_types:string_value(Content1), Last = case Content2 of <<>> -> <<>>; _ -> binary:last(Content2) end, case string:find(Content2, <<"--">>) == nomatch andalso Last =/= $- of true -> {Id, Ctx1} = next_id(Ctx), NewId = {Ref, Id}, { Node#{ id => NewId, sv := Content2 }, Ctx1 }; _ -> ?err('XQDY0072') end; contruct_node( Ctx, #{ nk := namespace, nn := {Ns, Px} } = Content, Ref, _InScopeNs ) -> ok = check_computed_namespaces([{Px, Ns}]), {Id, Ctx1} = next_id(Ctx), NewId = {Ref, Id}, {Content#{id := NewId}, Ctx1}; contruct_node( Ctx, #{ nk := text, sv := Content } = Node, Ref, _InScopeNs ) -> %?dbg("Content ",Content ), case merge_text_content(Content, text) of [] -> {[], Ctx}; Content1 -> %?dbg("Content1",Content1), Content2 = xqerl_types:string_value(Content1), {Id, Ctx1} = next_id(Ctx), NewId = {Ref, Id}, { Node#{ id => NewId, sv := Content2 }, Ctx1 } end; contruct_node(Ctx, #{id := {_, _, _}} = Content, Ref, InScopeNs) -> Node = copy_node(Content), contruct_node(Ctx, Node, Ref, InScopeNs). copy_node([N]) -> copy_node(N); copy_node(#{id := {_, _, _}} = N) -> D = xqldb_nodes:deep_copy_node(N), copy_node(D, erlang:make_ref()); copy_node( #{ nk := _, pt := _ } = N ) -> (copy_node(N, erlang:make_ref()))#{pt := []}; copy_node(#{nk := _} = N) -> copy_node(N, erlang:make_ref()); copy_node(_) -> ?err('XUTY0013'). copy_node( #{ id := {_, I}, at := At, ch := Ch } = N, Ref ) -> N#{ id := {Ref, I}, at := [copy_node(A, Ref) || A <- At], ch := [copy_node(A, Ref) || A <- Ch] }; copy_node( #{ id := {_, I}, ch := Ch } = N, Ref ) -> N#{ id := {Ref, I}, ch := [copy_node(A, Ref) || A <- Ch] }; copy_node( #{ id := {_, I}, at := At } = N, Ref ) -> N#{ id := {Ref, I}, at := [copy_node(A, Ref) || A <- At] }; copy_node(#{id := {_, I}} = N, Ref) -> N#{id := {Ref, I}}. get_node_type(#{nk := Nk}) -> Nk. ensure_qname( #xqAtomicValue{ type = 'xs:QName', value = #qname{ namespace = N, prefix = P, local_name = L } }, _InScopeNamespaces ) -> {N, P, L}; ensure_qname({Nx, Px}, InScopeNamespaces) -> #qname{namespace = N, prefix = P} = ensure_qname(#qname{namespace = Nx, prefix = Px}, InScopeNamespaces), {N, P}; ensure_qname([H], InScopeNamespaces) -> ensure_qname(H, InScopeNamespaces); ensure_qname(#{nk := _} = Node, InScopeNamespaces) -> ensure_qname(xqerl_types:atomize(Node), InScopeNamespaces); ensure_qname( #xqAtomicValue{ type = 'xs:untypedAtomic', value = Val }, InScopeNamespaces ) -> ensure_qname(Val, InScopeNamespaces); ensure_qname(Val, InScopeNamespaces) when is_binary(Val) -> try #qname{ namespace = N, prefix = P, local_name = L } = xqerl_types:value(xqerl_types:cast_as(Val, 'xs:QName', InScopeNamespaces)), {N, P, L} catch _:_ -> ?err('XQDY0074') end; ensure_qname(#xqAtomicValue{} = V, InScopeNamespaces) -> ?dbg("XPTY0004", {V, InScopeNamespaces}), ?err('XPTY0004'); ensure_qname(V, InScopeNamespaces) when is_number(V); is_atom(V) -> ?dbg("XPTY0004", {V, InScopeNamespaces}), ?err('XPTY0004'); ensure_qname(#qname{namespace = [Nx]} = QName, InScopeNamespaces) -> ensure_qname(QName#qname{namespace = Nx}, InScopeNamespaces); ensure_qname(#qname{prefix = [Nx]} = QName, InScopeNamespaces) -> ensure_qname(QName#qname{prefix = Nx}, InScopeNamespaces); ensure_qname(#qname{prefix = []} = QName, InScopeNamespaces) -> ensure_qname(QName#qname{prefix = <<>>}, InScopeNamespaces); ensure_qname(#qname{local_name = [Nx]} = QName, InScopeNamespaces) -> ensure_qname(QName#qname{local_name = Nx}, InScopeNamespaces); ensure_qname(#qname{namespace = #{nk := _} = Nx} = QName, InScopeNamespaces) -> NewNx = xqerl_types:string_value(Nx), ensure_qname(QName#qname{namespace = NewNx}, InScopeNamespaces); ensure_qname( #qname{namespace = #xqAtomicValue{} = Nx} = QName, InScopeNamespaces ) -> NewNx = xqerl_types:string_value(Nx), ensure_qname(QName#qname{namespace = NewNx}, InScopeNamespaces); ensure_qname(#qname{prefix = #{nk := _} = Nx} = QName, InScopeNamespaces) -> Atom = xqerl_types:atomize(Nx), ensure_qname(QName#qname{prefix = Atom}, InScopeNamespaces); ensure_qname( #qname{prefix = #xqAtomicValue{value = <<>>}} = QName, InScopeNamespaces ) -> ensure_qname(QName#qname{prefix = <<>>}, InScopeNamespaces); ensure_qname( #qname{prefix = #xqAtomicValue{type = AT} = Nx} = QName, InScopeNamespaces ) when ?xs_string(AT); AT == 'xs:untypedAtomic' -> NewNx = xqerl_types:string_value(Nx), ensure_qname(QName#qname{prefix = NewNx}, InScopeNamespaces); ensure_qname(#qname{prefix = #xqAtomicValue{}}, _) -> ?err('XPTY0004'); ensure_qname(#qname{local_name = #{nk := _} = Nx} = QName, InScopeNamespaces) -> Atom = xqerl_types:atomize(Nx), ensure_qname(QName#qname{local_name = Atom}, InScopeNamespaces); ensure_qname( #qname{local_name = #xqAtomicValue{value = <<>>}} = QName, InScopeNamespaces ) -> ensure_qname(QName#qname{local_name = <<>>}, InScopeNamespaces); ensure_qname( #qname{local_name = #xqAtomicValue{type = AT} = Nx} = QName, InScopeNamespaces ) when ?xs_string(AT); AT == 'xs:untypedAtomic' -> NewNx = xqerl_types:string_value(Nx), ensure_qname(QName#qname{local_name = NewNx}, InScopeNamespaces); ensure_qname(#qname{local_name = #xqAtomicValue{}}, _) -> ?err('XPTY0004'); ensure_qname( #qname{ namespace = Ns, prefix = Px, local_name = Ln }, _InScopeNamespaces ) when is_binary(Ns), is_binary(Px), is_binary(Ln) -> try Px1 = case Px of <<>> -> <<>>; _ -> xqerl_types:string_value(xqerl_types:cast_as(Px, 'xs:NCName')) end, Ln1 = xqerl_types:string_value(xqerl_types:cast_as(Ln, 'xs:NCName')), {Ns, Px1, Ln1} catch _:_ -> ?dbg("XQDY0074", {Ns, Px, Ln}), ?err('XQDY0074') end; ensure_qname( #qname{ namespace = Ns, prefix = <<>>, local_name = undefined } = QName, _InScopeNamespaces ) when is_binary(Ns) orelse is_atom(Ns) -> QName; ensure_qname( #qname{ prefix = <<"xml">>, local_name = undefined } = Q, _InScopeNamespaces ) -> Q; ensure_qname( #qname{ namespace = Ns, prefix = Px, local_name = undefined }, _InScopeNamespaces ) when is_binary(Ns), is_binary(Px) -> try %?dbg("Px",Px), Px1 = xqerl_types:string_value(xqerl_types:cast_as(Px, 'xs:NCName')), #qname{ namespace = Ns, prefix = Px1, local_name = undefined } catch _:_ -> ?dbg("XQDY0074", {Ns, Px}), ?err('XQDY0074') end; ensure_qname(QName, InScopeNamespaces) when is_list(QName) -> case xqerl_seq3:flatten(QName) of [H] -> ensure_qname(H, InScopeNamespaces); _ -> ?dbg("XPTY0004", QName), ?err('XPTY0004') end; ensure_qname(QName, _InScopeNamespaces) -> ?dbg("XPTY0004", QName), ?err('XPTY0004'). %% A namespace binding is created for each namespace declared in the current %% element constructor by a namespace declaration attribute. %% A namespace binding is created for each namespace node in the content %% sequence of the current element constructor. %% A namespace binding is created for each namespace that is declared in a %% namespace declaration attribute of an enclosing %% direct element constructor and not overridden by the current element %% constructor or an intermediate constructor. %% A namespace binding is always created to bind the prefix xml to the %% namespace URI http://www.w3.org/XML/1998/namespace. %% For each prefix used in the name of the constructed element or in the names %% of its attributes, a namespace binding must exist. %% If a namespace binding does not already exist for one of these prefixes, %% a new namespace binding is created for it. %% If this would result in a conflict, because it would require two different %% bindings of the same prefix, then the prefix used in the node name is %% changed to an arbitrary implementation-dependent prefix that does not %% cause such a conflict, and a namespace binding is created for this new %% prefix. If there is an in-scope default namespace, then a binding is %% created between the empty prefix and that URI. % return new in-scope namespaces check_element_name({_, <<"xmlns">>, _}) -> ?err('XQDY0096'); check_element_name({?XMLNS, _, _}) -> ?err('XQDY0096'); check_element_name({TNs, <<"xml">>, _}) when TNs =/= ?XML -> ?err('XQDY0096'); check_element_name({?XML, TPx, _}) when TPx =/= <<"xml">> -> ?err('XQDY0096'); check_element_name(_) -> ok. check_attribute_name(#{nn := {_, <<"xmlns">>, _}}, _) -> ?err('XQDY0044'); check_attribute_name(#{nn := {_, <<>>, <<"xmlns">>}}, _) -> ?err('XQDY0044'); check_attribute_name(#{nn := {?XMLNS, _, _}}, _) -> ?err('XQDY0044'); check_attribute_name(#{nn := {Ns, <<"xml">>, _}}, _) when Ns =/= ?XML -> ?err('XQDY0044'); check_attribute_name(#{nn := {?XML, Px, _}}, _) when Px =/= <<"xml">> -> ?err('XQDY0044'); check_attribute_name(#{nn := {Ns, _, Ln}}, Map) -> Key = {Ns, Ln}, case Map of #{Key := _} -> ?err('XQDY0025'); _ -> Map#{Key => ok} end. check_attribute_names([]) -> []; check_attribute_names(Atts) -> _ = lists:foldl(fun check_attribute_name/2, #{}, Atts), Atts. merge_content([]) -> []; merge_content(Content) when is_list(Content) -> %?dbg("Content",Content), Content1 = merge_content_1(Content), %?dbg("Content1", Content1), merge_content(Content1, []); merge_content(Content) when not is_list(Content) -> merge_content([Content]). merge_content_1(List) -> F = fun (#{nk := document, id := {_, _, _}} = N) -> xqldb_mem_nodes:children_no_p(copy_node(N)); (#{id := {_, _, _}} = N) -> [copy_node(N)]; (#{nk := text, sv := <<>>}) -> [{}]; (#{nk := document, ch := []}) -> [{}]; (#{nk := document, ch := E}) -> E; ([]) -> [{}]; (L) when is_list(L) -> maybe_merge_seq(merge_content_1(L), []); (A) when is_binary(A); is_number(A); is_boolean(A) -> [xqerl_types:cast_as(A, 'xs:untypedAtomic')]; (#xqAtomicValue{} = A) -> [xqerl_types:cast_as(A, 'xs:untypedAtomic')]; (A) when ?IS_ARRAY(A) -> merge_content_1(xqerl_mod_array:flatten(#{}, A)); (#xqFunction{}) -> ?err('XQTY0105'); (O) -> [O] end, lists:flatmap(F, List). merge_content([], Acc) -> lists:reverse(Acc); merge_content( [ #xqAtomicValue{value = St1}, #xqAtomicValue{value = St2} | T ], Acc ) -> Str3 = <>, merge_content([?UNTYP(Str3) | T], Acc); merge_content( [ #{nk := text, sv := St1}, #{nk := text, sv := St2} = H2 | T ], Acc ) -> Str3 = <>, Node = H2#{sv := Str3}, merge_content([Node | T], Acc); merge_content( [ #xqAtomicValue{value = St1}, #{nk := text, sv := St2} = H2 | T ], Acc ) -> Str3 = <>, Node = H2#{sv := Str3}, merge_content([Node | T], Acc); merge_content( [ #{nk := text, sv := St1} = H1, #xqAtomicValue{value = St2} | T ], Acc ) -> Str3 = <>, Node = H1#{sv := Str3}, merge_content([Node | T], Acc); merge_content([{} | T], Acc) -> merge_content(T, Acc); merge_content([#xqAtomicValue{value = <<>>}, {} | T], Acc) -> merge_content(T, Acc); merge_content([#xqAtomicValue{value = Val}, {} | T], Acc) -> merge_content(T, [#{nk => text, sv => Val} | Acc]); merge_content([#xqAtomicValue{value = <<>>} | T], Acc) -> merge_content(T, Acc); merge_content([#xqAtomicValue{value = Val} | T], Acc) -> merge_content(T, [#{nk => text, sv => Val} | Acc]); merge_content([H | T], Acc) -> merge_content(T, [H | Acc]). maybe_merge_seq([], Acc) -> lists:reverse(Acc); maybe_merge_seq([#{id := {_, _, _}} = F | T], Acc) -> H = copy_node(F), maybe_merge_seq([H | T], Acc); maybe_merge_seq([#{nk := document, ch := Ch} | T], Acc) -> maybe_merge_seq([Ch | T], Acc); maybe_merge_seq([H1, H2 | T], Acc) when is_binary(H1), is_binary(H2); is_binary(H1), is_number(H2); is_binary(H1), is_atom(H2); is_binary(H1), is_record(H2, xqAtomicValue); is_number(H1), is_binary(H2); is_number(H1), is_number(H2); is_number(H1), is_atom(H2); is_number(H1), is_record(H2, xqAtomicValue); is_atom(H1), is_binary(H2); is_atom(H1), is_number(H2); is_atom(H1), is_atom(H2); is_atom(H1), is_record(H2, xqAtomicValue); is_record(H1, xqAtomicValue), is_binary(H2); is_record(H1, xqAtomicValue), is_number(H2); is_record(H1, xqAtomicValue), is_atom(H2); is_record(H1, xqAtomicValue), is_record(H2, xqAtomicValue) -> St1 = xqerl_types:string_value(H1), St2 = xqerl_types:string_value(H2), Str3 = <>, maybe_merge_seq([?UNTYP(Str3) | T], Acc); maybe_merge_seq([H | T], Acc) when is_binary(H); is_number(H); is_atom(H); is_record(H, xqAtomicValue) -> St1 = xqerl_types:string_value(H), maybe_merge_seq([#{nk => text, sv => St1} | T], Acc); maybe_merge_seq([H | T], Acc) when ?IS_ARRAY(H) -> L = xqerl_mod_array:flatten(#{}, H), maybe_merge_seq([L | T], Acc); maybe_merge_seq([H | T], Acc) when is_list(H) -> maybe_merge_seq(maybe_merge_seq(H, []) ++ T, Acc); maybe_merge_seq([H | T], Acc) -> maybe_merge_seq(T, [H | Acc]). maybe_merge_text_seq(Seq) when is_list(Seq) -> Seq1 = [ case I of #{nk := _} -> atomize_node(I); _ -> I end || I <- Seq ], maybe_merge_text_seq_1(Seq1); maybe_merge_text_seq(Seq) -> maybe_merge_text_seq([Seq]). maybe_merge_text_seq_1([]) -> []; maybe_merge_text_seq_1([H1]) when is_binary(H1) -> H1; maybe_merge_text_seq_1([H1, H2 | T]) -> St1 = xqerl_types:string_value(H1), St2 = xqerl_types:string_value(H2), St3 = <>, maybe_merge_text_seq_1([St3 | T]); maybe_merge_text_seq_1([H1]) -> [xqerl_types:string_value(H1)]. merge_text_content([], _) -> []; merge_text_content([[]], _) -> []; merge_text_content(Content, Type) when is_list(Content) -> %?dbg("C1", Content), Content1 = merge_text_content_1(Content, Type), %?dbg("C2", Content1), Content1; merge_text_content(Content, Type) -> merge_text_content_1([Content], Type). merge_text_content_1([?UNTYP(V)], _) -> V; merge_text_content_1([H1, H2 | T], Type) when is_list(H1), is_binary(H2); is_list(H1), is_boolean(H2); is_list(H1), is_atom(H2); is_list(H1), is_number(H2); is_list(H1), is_record(H2, xqAtomicValue) -> H3 = maybe_merge_text_seq(H1), St2 = xqerl_types:string_value(H3), S1 = xqerl_types:string_value(H2), Str3 = ?UNTYP(<>), merge_text_content_1([Str3 | T], Type); merge_text_content_1([H1, H2 | T], Type) when is_list(H2), is_binary(H1); is_list(H2), is_boolean(H1); is_list(H2), is_atom(H1); is_list(H2), is_number(H1); is_list(H2), is_record(H1, xqAtomicValue) -> H3 = maybe_merge_text_seq(H2), St2 = xqerl_types:string_value(H3), S1 = xqerl_types:string_value(H1), Str3 = ?UNTYP(<>), merge_text_content_1([Str3 | T], Type); merge_text_content_1([H1 | T], Type) when is_list(H1) -> H3 = maybe_merge_text_seq(H1), St2 = xqerl_types:string_value(H3), Str3 = ?UNTYP(St2), merge_text_content_1([Str3 | T], Type); merge_text_content_1([?UNTYP(_) = H1, H2 | T], text) when is_binary(H2); is_boolean(H2); is_atom(H2); is_number(H2); is_record(H2, xqAtomicValue) -> St1 = xqerl_types:string_value(H1), St2 = xqerl_types:string_value(xqerl_types:cast_as(H2, 'xs:untypedAtomic')), Str3 = <>, merge_text_content_1([Str3 | T], text); merge_text_content_1([?UNTYP(_) = H1, H2 | T], attribute) when is_binary(H2); is_boolean(H2); is_atom(H2); is_number(H2); is_record(H2, xqAtomicValue) -> St1 = xqerl_types:string_value(H1), St2 = xqerl_types:string_value(xqerl_types:cast_as(H2, 'xs:untypedAtomic')), Str3 = <>, merge_text_content_1([Str3 | T], attribute); merge_text_content_1([#{nk := Nk} = H | T], Type) when Nk == document; Nk == element; Nk == attribute; Nk == comment; Nk == text; Nk == 'processing-instruction' -> Atomized = atomize_node(H), merge_text_content_1([Atomized | T], Type); merge_text_content_1([#xqAtomicValue{type = Type} = H | T], Kind) when Type =/= 'xs:untypedAtomic' -> NewH = xqerl_types:cast_as(H, 'xs:untypedAtomic'), merge_text_content_1([NewH | T], Kind); merge_text_content_1([H | T], Type) when is_binary(H); is_boolean(H); is_atom(H); is_number(H) -> NewH = xqerl_types:cast_as(H, 'xs:untypedAtomic'), merge_text_content_1([NewH | T], Type); merge_text_content_1([H1, #{nk := Nk} = H2 | T], Type) when Nk == document; Nk == element; Nk == attribute; Nk == comment; Nk == text; Nk == 'processing-instruction' -> Atomized = atomize_node(H2), merge_text_content_1([H1, Atomized | T], Type). % a unique hash value for this node get_node_hash(#{nk := _} = Node) -> erlang:phash2(Node, 4294967296); get_node_hash(_NonNode) -> ?err('XPTY0004'). % string values nodes as the correct type atomize_nodes(List) when is_list(List) -> lists:map( fun(I) -> atomize_node(I) end, List ); atomize_nodes(List) -> [atomize_node(List)]. atomize_node([]) -> <<>>; atomize_node([S]) -> atomize_node(S); atomize_node(#xqAtomicValue{} = Av) -> Av; atomize_node(Bin) when is_binary(Bin) -> Bin; atomize_node(#{nk := _} = Node) -> xqerl_types:atomize(Node). atomize_node(Ctx, F) when is_function(F, 1) -> atomize_node(Ctx, F(Ctx)); atomize_node(Ctx, #{nk := element, ch := S}) -> atomize_node(Ctx, S); atomize_node(_, Bin) when is_binary(Bin) -> Bin; atomize_node(_, N) -> atomize_node(N). names_equal({N1, _, L1}, {N1, _, L1}) -> true; names_equal(_, _) -> false. nodes_equal(Node1, Node2, Collation) -> nodes_equal_1(Node1, Node2, Collation). %% Tests if 2 nodes are logically deep-equal nodes_equal_1( #{ nk := _, id := {_, _, _} } = N1, N2, C ) -> Copy = xqldb_nodes:deep_copy_node(N1), nodes_equal_1(Copy, N2, C); nodes_equal_1( N1, #{ nk := _, id := {_, _, _} } = N2, C ) -> Copy = xqldb_nodes:deep_copy_node(N2), nodes_equal_1(N1, Copy, C); nodes_equal_1(#{nk := _} = N1, #{nk := _} = N1, _) -> true; nodes_equal_1( #{nk := document} = Node1, #{nk := document} = Node2, Collation ) -> Nodes1 = get_comparable_child_nodes(Node1), Nodes2 = get_comparable_child_nodes(Node2), length(Nodes1) == length(Nodes2) andalso lists:all( fun({Ci1, Ci2}) -> nodes_equal_1(Ci1, Ci2, Collation) end, lists:zip(Nodes1, Nodes2) ); nodes_equal_1( #{nk := fragment} = Node1, #{nk := fragment} = Node2, Collation ) -> attributes_equal( xqldb_mem_nodes:attributes(Node1), xqldb_mem_nodes:attributes(Node2), Collation ) andalso begin Nodes1 = get_comparable_child_nodes(Node1), Nodes2 = get_comparable_child_nodes(Node2), %?dbg("{Nodes1,Nodes2}",{Nodes1,Nodes2}), length(Nodes1) == length(Nodes2) andalso lists:all( fun({Ci1, Ci2}) -> nodes_equal_1(Ci1, Ci2, Collation) end, lists:zip(Nodes1, Nodes2) ) end; nodes_equal_1( #{nk := element} = Node1, #{nk := element} = Node2, Collation ) -> Name1 = xqldb_mem_nodes:node_name(Node1), Name2 = xqldb_mem_nodes:node_name(Node2), %?dbg("{Name1,Name2}",{Name1,Name2}), names_equal(Name1, Name2) andalso attributes_equal( xqldb_mem_nodes:attributes(Node1), xqldb_mem_nodes:attributes(Node2), Collation ) andalso begin Nodes1 = get_comparable_child_nodes(Node1), Nodes2 = get_comparable_child_nodes(Node2), %?dbg("{Nodes1,Nodes2}",{Nodes1,Nodes2}), length(Nodes1) == length(Nodes2) andalso lists:all( fun({Ci1, Ci2}) -> nodes_equal_1(Ci1, Ci2, Collation) end, lists:zip(Nodes1, Nodes2) ) end; nodes_equal_1( #{nk := attribute} = Node1, #{nk := attribute} = Node2, Collation ) -> Name1 = xqldb_mem_nodes:node_name(Node1), Name2 = xqldb_mem_nodes:node_name(Node2), %?dbg("{Name1,Name2}",{Name1,Name2}), names_equal(Name1, Name2) andalso begin Val1 = xqerl_lib:decode_string( xqldb_mem_nodes:string_value(Node1) ), Val2 = xqerl_lib:decode_string( xqldb_mem_nodes:string_value(Node2) ), %?dbg("{Val1,Val2}",{Val1,Val2}), xqerl_operators:equal( Val1, Val2, Collation ) end; nodes_equal_1( #{nk := text} = Node1, #{nk := text} = Node2, Collation ) -> Val1 = xqerl_lib:decode_string(xqldb_mem_nodes:string_value(Node1)), Val2 = xqerl_lib:decode_string(xqldb_mem_nodes:string_value(Node2)), xqerl_operators:equal(Val1, Val2, Collation); nodes_equal_1( #{nk := comment} = Node1, #{nk := comment} = Node2, Collation ) -> Val1 = xqldb_mem_nodes:string_value(Node1), Val2 = xqldb_mem_nodes:string_value(Node2), xqerl_operators:equal(Val1, Val2, Collation); nodes_equal_1( #{nk := 'processing-instruction'} = Node1, #{nk := 'processing-instruction'} = Node2, _Collation ) -> Name1 = xqldb_mem_nodes:node_name(Node1), Name2 = xqldb_mem_nodes:node_name(Node2), names_equal(Name1, Name2) andalso begin Val1 = xqldb_mem_nodes:string_value(Node1), Val2 = xqldb_mem_nodes:string_value(Node2), xqerl_operators:equal(Val1, Val2) end; nodes_equal_1(_, _, _) -> false. % takes actual nodes, not ids attributes_equal([], [], _) -> true; attributes_equal(Atts1, Atts2, Collation) -> %?dbg("{Atts1,Atts2}",{Atts1,Atts2}), length(Atts1) == length(Atts2) andalso lists:all( fun(A1) -> lists:any( fun(A2) -> nodes_equal_1(A1, A2, Collation) end, Atts2 ) end, Atts1 ). get_comparable_child_nodes(#{ch := Children}) -> [ C || #{nk := Nk} = C <- Children, Nk =/= comment, Nk =/= 'processing-instruction' ]; get_comparable_child_nodes(_) -> []. to_xml(#xqError{} = E) -> E; to_xml(A) when ?IS_ARRAY(A) -> to_xml(array:to_list(A)); to_xml(N) when is_list(N) -> M = combine_atomics(N, []), << <<(to_xml(M1))/binary>> || M1 <- M >>; to_xml( #{ nk := _, id := {_, _, _} } = Node ) -> % deep copy DB nodes Copy = xqldb_nodes:deep_copy_node(Node), xqerl_serialize:serialize(Copy, #{method => xml}); to_xml(#{nk := _} = Node) -> xqerl_serialize:serialize(Node, #{method => xml}); to_xml(#xqAtomicValue{} = A) -> xqerl_types:string_value(A); to_xml(B) when is_binary(B) -> B; to_xml(V) when is_atom(V); is_number(V) -> xqerl_types:string_value(V). combine_atomics([], Acc) -> lists:reverse(Acc); combine_atomics([H1, H2 | T], Acc) when is_record(H1, xqAtomicValue) orelse is_binary(H1) orelse is_atom(H1) orelse is_number(H1), is_record(H2, xqAtomicValue) orelse is_binary(H2) orelse is_atom(H2) orelse is_number(H2) -> St1 = xqerl_types:string_value(H1), St2 = xqerl_types:string_value(xqerl_types:cast_as(H2, 'xs:string')), Str3 = <>, combine_atomics([Str3 | T], Acc); combine_atomics([H | T], Acc) -> combine_atomics(T, [H | Acc]). % check if element is in no namespace and it is being reset by a % computed namespace check_computed_default_override({<<>>, _, _}, ComputNamespaces) -> case [ok || {<<>>, _} <- ComputNamespaces] of [] -> ok; _ -> ?dbg("Namespaces", ComputNamespaces), ?err('XQDY0102') end; check_computed_default_override(_, _) -> ok. expand_content(Ctx, ContentFun) when is_function(ContentFun, 1) -> ContentFun(Ctx); expand_content(_, Content) -> Content. do_namespace_augmentation(true, Node, Preserve, Inherit, IsUpd, InScopeNs, Nss) -> #{ns := InScopeNs0} = Node, case {Preserve, Inherit} of {preserve, inherit} when IsUpd -> % inscopes switched here, parent wins instead of child augment_direct_inscope_namespaces(Nss, maps:merge(InScopeNs, InScopeNs0)); {preserve, inherit} -> %?dbg("InScopeNs0", InScopeNs0), %?dbg("InScopeNs ", InScopeNs ), augment_direct_inscope_namespaces(Nss, maps:merge(InScopeNs0, InScopeNs)); {preserve, 'no-inherit'} -> augment_direct_inscope_namespaces(Nss, InScopeNs0); {'no-preserve', inherit} -> augment_direct_inscope_namespaces(Nss, InScopeNs); {'no-preserve', 'no-inherit'} -> augment_direct_inscope_namespaces(Nss, #{}) end; do_namespace_augmentation(false, _Node, _Preserve, _Inherit, _IsUpd, InScopeNs, Nss) -> augment_direct_inscope_namespaces(Nss, InScopeNs).