📄 xmerl_xpath_pred.erl
字号:
%%% The contents of this file are subject to the Erlang Public License,%%% Version 1.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.erlang.org/license/EPL1_0.txt%%%%%% Software distributed under the License is distributed on an "AS IS"%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%%% the License for the specific language governing rights and limitations%%% under the License.%%%%%% The Original Code is xmerl-0.6%%%%%% The Initial Developer of the Original Code is Ericsson Telecom%%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson%%% Telecom AB. All Rights Reserved.%%%%%% Contributor(s): ______________________________________.%%%%%%----------------------------------------------------------------------%%% #0. BASIC INFORMATION%%%----------------------------------------------------------------------%%% @private%%% File: xmerl_xpath_pred.erl%%% Author : Ulf Wiger <ulf.wiger@ericsson.com>%%% Description : Helper module to xmerl_xpath: XPATH predicates.%%% %%% Modules used : lists, string, xmerl_scan, xmerl_xpath%%% %%%-----------------------------------------------------------------------module(xmerl_xpath_pred).-vsn('0.6').-date('00-09-22').-author('ulf.wiger@ericsson.com').%% API-export([eval/2]).%% internal functions (called via apply/3)-export([boolean/1, boolean/2, ceiling/2, concat/2, contains/2, count/2, floor/2, fn_false/2, fn_not/2, fn_true/2, id/2, lang/2, last/2, 'local-name'/2, 'namespace-uri'/2, nodeset/1, 'normalize-space'/2, number/1, number/2, position/2, round/2, 'starts-with'/2, string/1, 'string-length'/2, substring/2, 'substring-after'/2, 'substring-before'/2, sum/2, translate/2]). -include("xmerl.hrl").-include("xmerl_xpath.hrl").%% -record(obj, {type,%% value}).-define(string(X), #xmlObj{type = string, value = X}).-define(nodeset(X), #xmlObj{type = nodeset, value = X}).-define(number(X), #xmlObj{type = number, value = X}).-define(boolean(X), #xmlObj{type = boolean, value = X}).eval(Expr, C = #xmlContext{context_node = #xmlNode{pos = Pos}}) -> Obj = expr(Expr, C), Res = case Obj#xmlObj.type of number when Obj#xmlObj.value == Pos -> true; number -> false; boolean -> Obj#xmlObj.value; _ -> mk_boolean(C, Obj) end,% io:format("eval(~p, ~p) -> ~p~n", [Expr, Pos, Res]), Res.string(X) -> ?string(X).nodeset(X) -> ?nodeset(X).number(X) -> ?number(X).boolean(X) -> ?boolean(X).expr({arith, Op, E1, E2}, C) -> arith_expr(Op, E1, E2, C);expr({comp, Op, E1, E2}, C) -> comp_expr(Op, E1, E2, C);expr({bool, Op, E1, E2}, C) -> bool_expr(Op, E1, E2, C);expr({'negative', E}, C) -> N = mk_number(C, E), - N;expr({number, N}, _C) -> ?number(N);expr({literal, S}, _C) -> ?string(S);expr({function_call, F, Args}, C) -> case core_function(F) of {true, F1} -> ?MODULE:F1(C, Args); true -> ?MODULE:F(C, Args); false -> %% here, we should look up the function in the context provided %% by the caller, but we haven't figured this out yet. exit({not_a_core_function, F}) end;expr({path, Type, PathExpr}, C) -> #state{context=#xmlContext{nodeset = NS}} = xmerl_xpath:eval_path(Type, PathExpr, C), ?nodeset(NS);expr(Expr, _C) -> exit({unknown_expr, Expr}).arith_expr('+', E1, E2, C) -> ?number(mk_number(C, E1) + mk_number(C, E2));arith_expr('-', E1, E2, C) -> ?number(mk_number(C, E1) - mk_number(C, E2));arith_expr('*', E1, E2, C) -> ?number(mk_number(C, E1) * mk_number(C, E2));arith_expr('div', E1, E2, C) -> ?number(mk_number(C, E1) / mk_number(C, E2));arith_expr('mod', E1, E2, C) -> ?number(mk_number(C, E1) rem mk_number(C, E2)).comp_expr('>', E1, E2, C) -> N1 = expr(E1,C), N2 = expr(E2,C), ?boolean(compare_ineq_format(N1,N2,C) > compare_ineq_format(N2,N1,C));comp_expr('<', E1, E2, C) -> N1 = expr(E1,C), N2 = expr(E2,C), ?boolean(compare_ineq_format(N1,N2,C) > compare_ineq_format(N2,N1,C));comp_expr('>=', E1, E2, C) -> N1 = expr(E1,C), N2 = expr(E2,C), ?boolean(compare_ineq_format(N1,N2,C) > compare_ineq_format(N2,N1,C));comp_expr('<=', E1, E2, C) -> N1 = expr(E1,C), N2 = expr(E2,C), ?boolean(compare_ineq_format(N1,N2,C) > compare_ineq_format(N2,N1,C));comp_expr('=', E1, E2, C) -> N1 = expr(E1,C), N2 = expr(E2,C), ?boolean(compare_eq_format(N1,N2,C) == compare_eq_format(N2,N1,C));comp_expr('!=', E1, E2, C) -> N1 = expr(E1,C), N2 = expr(E2,C), ?boolean(compare_eq_format(N1,N2,C) == compare_eq_format(N2,N1,C)).bool_expr('or', E1, E2, C) -> ?boolean(mk_boolean(C, E1) or mk_boolean(C, E2));bool_expr('and', E1, E2, C) -> ?boolean(mk_boolean(C, E1) and mk_boolean(C, E2)).%% According to chapter 3.4 in XML Path Language ver 1.0 the format of%% the compared objects are depending on the type of the other%% object. %% 1. Comparisons involving node-sets is treated equally despite%% of which comparancy operand is used. In this case:%% - node-set comp node-set: string values are used%% - node-set comp number : ((node-set string value) -> number) %% - node-set comp boolean : (node-set string value) -> boolean%% 2. Comparisons when neither object is a node-set and the operand%% is = or != the following transformation is done before comparison:%% - if one object is a boolean the other is converted to a boolean.%% - if one object is a number the other is converted to a number.%% - otherwise convert both to the string value.%% 3. Comparisons when neither object is a node-set and the operand is%% <=, <, >= or > both objects are converted to a number.compare_eq_format(N1=#xmlObj{type=T1},N2=#xmlObj{type=T2},C) when T1==nodeset; T2==nodeset -> compare_nseq_format(N1,N2,C);compare_eq_format(N1=#xmlObj{type=T1},#xmlObj{type=T2},C) when T1==boolean; T2==boolean -> mk_boolean(C,N1);compare_eq_format(N1=#xmlObj{type=T1},#xmlObj{type=T2},C) when T1==number; T2==number -> mk_number(C,N1);compare_eq_format(N1,_,C) -> mk_string(C,string_value(N1)).compare_ineq_format(N1=#xmlObj{type=T1}, N2=#xmlObj{type=T2},C) when T1==nodeset; T2==nodeset -> compare_nseq_format(N1,N2,C);compare_ineq_format(N1,_N2,C) -> mk_number(C,N1).compare_nseq_format(N1=#xmlObj{type = number},_N2,C) -> mk_number(C,N1);compare_nseq_format(N1=#xmlObj{type = boolean},_N2,C) -> mk_boolean(C,N1);compare_nseq_format(N1=#xmlObj{type = string},_N2,C) -> mk_string(C,N1);compare_nseq_format(N1=#xmlObj{type = nodeset},_N2=#xmlObj{type=number},C) -> %% transform nodeset value to its string-value mk_number(C,string_value(N1));compare_nseq_format(N1=#xmlObj{type = nodeset},_N2=#xmlObj{type=boolean},C) -> mk_boolean(C,N1);compare_nseq_format(N1=#xmlObj{type = nodeset},_N2,C) -> mk_string(C,string_value(N1)).core_function('last') -> true;core_function('position') -> true;core_function('count') -> true;core_function('id') -> true;core_function('local-name') -> true;core_function('namespace-uri') -> true;core_function('name') -> true;core_function('string') -> true;core_function('concat') -> true;core_function('starts-with') -> true;core_function('contains') -> true;core_function('substring-before') -> true;core_function('substring-after') -> true;core_function('string-length') -> true;core_function('normalize-space') -> true;core_function('translate') -> true;core_function('boolean') -> true;core_function('not') -> {true, fn_not};core_function('true') -> {true, fn_true};core_function('false') -> {true, fn_false};core_function('lang') -> true;core_function('number') -> true;core_function('sum') -> true;core_function('floor') -> true;core_function('ceiling') -> true;core_function('round') -> true;core_function(_) -> false.%%% node set functions%% number: last()last(#xmlContext{nodeset = Set}, []) -> ?number(length(Set)).%% number: position()position(#xmlContext{context_node = #xmlNode{pos = Pos}}, []) -> ?number(Pos).%% number: count(node-set)count(C, [Arg]) -> ?number(length(mk_nodeset(C, Arg))).%% node-set: id(object)id(C, [Arg]) -> NS0 = [C#xmlContext.whole_document], case Arg#xmlObj.type of nodeset -> NodeSet = Arg#xmlObj.value, IdTokens = lists:foldl( fun(N, AccX) -> StrVal = string_value(N), TokensX = id_tokens(StrVal), TokensX ++ AccX end, [], NodeSet), NewNodeSet = xmerl_xpath:axis(descendant_or_self, fun(Node) -> attribute_test(Node, id, IdTokens) end, C#xmlContext{nodeset = NS0}), ?nodeset(NewNodeSet); _ -> StrVal = string_value(Arg#xmlObj.value), IdTokens = id_tokens(StrVal), lists:foldl( fun(Tok, AccX) -> select_on_attribute(NS0, id, Tok, AccX) end, [], IdTokens) end.id_tokens(Str) -> string:tokens(Str, " \t\n\r"). attribute_test(#xmlNode{node = #xmlElement{attributes = Attrs}}, Key, Vals) -> case lists:keysearch(Key, #xmlAttribute.name, Attrs) of {value, #xmlAttribute{value = V}} -> lists:member(V, Vals); _ -> false end;attribute_test(_Node, _Key, _Vals) -> false.%%% CONTINUE HERE!!!!%% string: local-name(node-set?)'local-name'(C, []) -> local_name1(default_nodeset(C));'local-name'(C, [Arg]) -> local_name1(mk_nodeset(C, Arg)).local_name1([]) -> ?string([]);local_name1([#xmlElement{name = Name, nsinfo = NSI}|_]) -> case NSI of {_Prefix, Local} -> ?string(Local); [] -> ?string(Name) end.%% string: namespace-uri(node-set?)'namespace-uri'(C, []) -> ns_uri(default_nodeset(C));'namespace-uri'(C, [Arg]) -> ns_uri(mk_nodeset(C, Arg)).ns_uri([]) -> ?string([]);ns_uri([#xmlElement{nsinfo = NSI, namespace = NS}|_]) -> case NSI of {Prefix, _} -> case lists:keysearch(Prefix, 1, NS#xmlNamespace.nodes) of false -> ?string([]); {value, {_K, V}} -> ?string(V) end; [] -> [] end.%%% String functions%% string: string(object?)string(C, []) -> ns_string(default_nodeset(C));string(C, [Arg]) -> string_value(mk_object(C, Arg)).ns_string([Obj|_]) ->
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -