Module | XTemplate::XPath |
In: |
lib/xtemplate/xpath.rb
|
PathSeparator | = | '/' |
PathUnion | = | '|' |
PathIfNot | = | '?' |
AnyNode | = | '*' |
AnyNode0 | = | '' |
AnyNode2 | = | '**' |
TextNode | = | nil |
RootNode | = | "\000" |
CurrentNode | = | "." |
ParentNode | = | ".." |
VALUE_BY_PATH1 | = | %q` # (1)path (2)current data (3)parent data (4)root data (5)plugin object def value_by_path(ids, data, pdata, rdata, plugin) data[ParentNode] ||= pdata hd,*ids = ids case hd when nil data when RootNode value_by_path(ids, rdata, nil, rdata, plugin) when CurrentNode value_by_path(ids, data, pdata, rdata, plugin) when ParentNode value_by_path(ids, data[ParentNode], nil, rdata, plugin) when AnyNode0 [ value_by_path(ids, data, pdata, rdata, plugin), value_by_path([AnyNode2]+ids, data, pdata, rdata, plugin) ].flatten when /^(@\*|\*\*|\*)([\[\{].+[\]\}])?$/ if( $1 == '@*' ) opt = $2 if( opt ) val = data.keys.select{|x| x[0] == ?@}.collect{|key| key = key + opt value_by_path([key]+ids, data, pdata, rdata, plugin) }.flatten else val = data.keys.select{|x| x[0] == ?@}.collect{|key| value_by_path([key]+ids, data, pdata, rdata, plugin) }.flatten end elsif( $1 == '*' ) opt = $2 if( opt ) val = data.keys.select{|x| !(x == ParentNode || x == TextNode) }.collect{|key| key = key + opt value_by_path([key]+ids, data, pdata, rdata, plugin) }.flatten else val = data.keys.reject{|x| (x == ParentNode || x == TextNode) }.collect{|key| value_by_path([key]+ids, data, pdata, rdata, plugin) }.flatten end else # case '**' opt = $2 ss = [] vals = [] d = value_depth(data) # {'1' => {'2' => {'3' => '...'}}} d = 3 # { * => { * => {'3' => '...'}}} if( opt ) for i in 0..(d - 2) ss.push(AnyNode + opt) v = value_by_path(ss+ids, data, pdata, rdata, plugin) ss.pop ss.push(AnyNode) vals.push(v) end else for i in 0..(d - 2) ss.push(AnyNode) v = value_by_path(ss+ids, data, pdata, rdata, plugin) vals.push(v) end end val = vals.flatten end val else ` |
VALUE_BY_PATH2_1 | = | %q` case hd[-1] when ?] hd,*opts = cond_split(hd) when ?} hd,*opts = cond_split(hd) else opts = [] end if( hd.size > 0 ) hd,recv,*rargs = hd.split(".") val = data[hd] if( recv ) if( recv =~ /(.+)\((.*)\)/ ) recv = $1 rargs = $2.split(',').collect{|c| eval_expr(c, data, plugin) } else rargs = [] end val = val.__send__(recv,*rargs) end else val = data end if( (val = normalize(val)).nil? ) return nil end for opt in opts case opt when /^\{(.+)\}/ val = eval_action($1.strip, val, plugin) when /^\[(\d+)\.\.\.(\d+)\]/ range = ($1.to_i ... $2.to_i) val = val[range] when /^\[(\d+)\.\.(\d+)\]/ range = ($1.to_i .. $2.to_i) val = val[range] when /^\[(\d+),(\d+)\]/ pos = $1.to_i n = $2.to_i val = val[pos,n] when /^\[(\d+)\]/ pos = $1.to_i val = val[pos] when /^\[(.+)\]/ cond = $1 case val when Array val = val.select{|n| eval_condition(cond, n, plugin) } else unless( eval_condition(cond,val,plugin) ) val = nil end end else raise(RuntimeError, "unknown path components: #{opt}") end if( (val = normalize(val)).nil? ) return nil end end # end of 'for' ` |
VALUE_BY_PATH2_2 | = | %q` if( hd.size > 0 ) hd,recv,*rargs = hd.split(".") val = data[hd] if( recv ) if( recv =~ /(.+)\((.*)\)/ ) recv = $1 rargs = $2.split(',').collect{|c| eval_expr(c, data, plugin) } else rargs = [] end val = val.__send__(recv,*rargs) end else val = data end val = normalize(val) ` |
VALUE_BY_PATH3 | = | %q` case val when Hash value_by_path(ids, val, data, rdata, plugin) when Array val.collect{|x| case x when Hash value_by_path(ids, x, data, rdata, plugin) else if( ids.empty? ) x else nil end end }.flatten when nil nil else if( ids.empty? ) # 'val' should be a text node, if 'ids' is empty. val else nil end end end # end of 'case hd' end # end of value_by_path() ` |
also implemented in xt.c
# File lib/xtemplate/xpath.rb, line 639 def args_split(args) args = unsanitize(args) i = 0 l = false s = 0 escape = false inref = false ids = [] args.each_byte{|c| case c when ?', ?" if( escape ) escape = false else if( l ) l = false else l = true end end when ?\\ escape = true when ?, if( !l ) ids.push(args[s..i].chop) s = i + 1 end end i += 1 } ids.push(args[s..i]) ids.collect{|s| s.strip.gsub(/(\A['"])|(["']\z)/,'') }.reject{|s| s.empty? } end
also implemented in xt.c
# File lib/xtemplate/xpath.rb, line 674 def cond_split(path) i = 0 l = 0 s = 0 xs = [] path.each_byte{|c| case c when ?{, ?[ if( l == 0 ) case i when 0 xs.push("") when s # do nothing else xs.push(path[s..(i-1)]) end s = i end l += 1 when ?}, ?] l -= 1 if( l == 0 ) xs.push(path[s..i]) s = i + 1 end end i += 1 } unless( s == i ) xs.push(path[s..i]) end xs end
# File lib/xtemplate/xpath.rb, line 1127 def eval_action(act, val, plugin) newval = nil act.strip! if( act.include?(";") ) newval = val act.split(";").each{|a| newval = eval_action(a.strip, newval, plugin) } return newval end if( act =~ /^([^\(\)]+)\(([^\(\)]*)\)$/ ) func = $1.strip args = args_split($2) newval = plugin.__send__(func,val,*args) else case val when Array newval = val.collect{|x| {act => x} } else newval = {act => val} end end newval end
# File lib/xtemplate/xpath.rb, line 1084 def eval_condition(expr, val, plugin) if( expr =~ /\s+or\s+/ ) expr.split(/\s+or\s+/).any?{|x| eval_condition(x.strip,val,plugin) } elsif( expr =~ /\s+and\s+/ ) expr.split(/\s+and\s+/).all?{|x| eval_condition(x.strip,val,plugin) } elsif( expr =~ /^not\s+(.+)$/ ) ! eval_condition($1.strip,val,plugin) else case expr when /^([^!=<>~\s\(\)]+(\([^!=<>~\s\(\)]*\))?)\s*(=|!=|<|>|<=|>=|=~|!~|<=?|>=?)\s*([^!=<>~\s\(\)]+(\([^!=<>~\s\(\)]*\))?)$/ lhs = eval_expr($1.strip,val,plugin) op = $3 rhs = eval_expr($4.strip,val,plugin) unless( lhs.nil? || rhs.nil? ) case op when '=' (lhs == rhs) when '<', '<' (lhs < rhs) when '<=', '<=' (lhs <= rhs) when '>', '>' (lhs > rhs) when '>=', '>=' (lhs >= rhs) when '=~' (lhs =~ rhs) when '!~' (lhs !~ rhs) else raise(NotImplementedError, "'#{op}'") end else false end when /^([^=<>~]+)$/ eval_expr($1.strip,val,plugin) else nil end end # end of 'else' end
# File lib/xtemplate/xpath.rb, line 1023 def eval_expr(expr, val, plugin) case expr when "text()" if( val.is_a?(Hash) && val[TextNode] && val.size == 1 ) val[TextNode] else val end when "size()" case val when Array val.size when nil 0 else 1 end when /int\((.+)\)/ eval_expr($1,val,plugin).to_i when /float\((.+)\)/ eval_expr($1,val,plugin).to_f when /^(-?\d+)$/ $1.to_i when /^(-?\d+)\.(\d+)$/ $1.to_f when /^('|"|"|')(.+)('|"|"|')$/ str = $2 str.gsub(/\\./){|m| $1} when /^%q\((.+)\)$/ str = $1 str.gsub(/\\./){|m| $1} when /^%r\((.+)\)$/, %r{/(.+)/} str = $1 str.gsub!(/\\./){|m| $1} Regexp.new(str) when 'nil' nil else path_split(expr).each{|path| case val when Hash val = val[path] if( val.is_a?(Hash) && val[TextNode] && val.size == 1 ) val = val[TextNode] end when Array val = val.collect{|v| eval_expr(path, v, plugin) }.flatten.reject{|v| v.nil?} if( val.size == 0 ) val = nil end else val = nil break end } val end end
# File lib/xtemplate/xpath.rb, line 772 def normalize(val) case val when Hash if( val[TextNode] && val.size == 1 ) val = val[TextNode] end when XArray val = val.reject{|v| v.is_a?(String) && (v =~ /\A\s*\z/) } if( val.size > 1 ) catch(:break){ h = {} node_p = false val.each{|v| case v when Hash v.each{|k,x| if( h[k] ) throw(:break) else h[k] = x unless( k && (k[0] == ?@) ) node_p = true end end } else if( h[TextNode] ) throw(:break) else h[TextNode] = v end end } if( h[TextNode] ) if( h.size == 1 ) h = h[TextNode] else if( node_p ) throw(:break) end end end val.clear val.push(h) } end when Array val.reject!{|x|x.nil?} end val end
also implemented in xt.c
# File lib/xtemplate/xpath.rb, line 612 def path_split(path) i = 0 l = 0 s = 0 ids = [] path.each_byte{|c| case c when ?{, ?[ l += 1 when ?}, ?] l -= 1 when ?/ if( l == 0 ) ids.push(path[s..i].chop) s = i + 1 end end i += 1 } ids.push(path[s..i]) if( path[0] == ?/ ) ids[0] = RootNode end ids end
# File lib/xtemplate/xpath.rb, line 1012 def use_default_xpath() XPath.module_eval(VALUE_BY_PATH1 + VALUE_BY_PATH2_1 + VALUE_BY_PATH3) end
# File lib/xtemplate/xpath.rb, line 1016 def use_simple_xpath() XPath.module_eval(VALUE_BY_PATH1 + VALUE_BY_PATH2_2 + VALUE_BY_PATH3) end
Returns an empty array instead of ‘nil’ to eliminate a tag.
# File lib/xtemplate/xpath.rb, line 725 def value_by_path2(path, data, pdata, rdata, plugin) if( path.is_a?(String) ) if( path =~ /([\?\|])/ ) case $1[0] when ?| paths = path.split(PathUnion) return paths.collect{|path| path.strip! value_by_path2(path, data, pdata, rdata, plugin) }.flatten when ?? paths = path.split(PathIfNot) for path in paths path.strip! vals = value_by_path2(path, data, pdata, rdata, plugin) if( vals.nil? || vals.empty? ) next end break end return vals end end ids = path_split(path) else ids = path.dup end x = value_by_path(ids, data, pdata, rdata, plugin) if( x ) case x when Array x.reject!{|e|e.nil?} if( x.size == 1 ) x[0] else x end else x end else [] end end
# File lib/xtemplate/xpath.rb, line 544 def value_depth(val) case val when Hash max = 0 val.each{|key,val| if( key == ParentNode ) next end if( (x = value_depth(val)) > max ) max = x end } max + 1 when Array max = 0 val.each{|val| if( (x = value_depth(val)) > max ) max = x end } max else 0 end end
# File lib/xtemplate/xpath.rb, line 529 def value_inspect(val) case val when Hash val = val.dup val.delete(ParentNode) "{" + val.collect{|k,v| "#{k.inspect}=>#{value_inspect(v)}" }.join(", ") + "}" when Array "#{val.class}[" + val.collect{|v| value_inspect(v)}.join(", ") + "]" when SanitizedString val.to_s.inspect else val.inspect end end
# File lib/xtemplate/xpath.rb, line 574 def value_to_xml(val, parent) case val when Hash val.each{|k,v| case k when ParentNode # do nothing when TextNode parent.add_child(v) when /^@(.+)/ parent.add_attr($1) parent.add_attrval(v) else node = XNode.new(k) value_to_xml(v,node) parent.add_child(node) end } when Array val = val.collect{|v| case v when Hash, Array v else {TextNode => v} end } val.each{|v| value_to_xml(v,parent) } when nil nil else parent.add_child(val) end end