Module XTemplate::XPath
In: lib/xtemplate/xpath.rb

Methods

Included Modules

Util

Classes and Modules

Class XTemplate::XPath::Action

Constants

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() `

Public Instance methods

also implemented in xt.c

[Source]

# 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

[Source]

# 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

[Source]

# 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

[Source]

# 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*(=|!=|<|>|<=|>=|=~|!~|&lt;=?|&gt;=?)\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 '&lt;', '<'
              (lhs < rhs)
            when '&lt;=', '<='
              (lhs <= rhs)
            when '&gt;', '>'
              (lhs > rhs)
            when '&gt;=', '>='
              (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

[Source]

# 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 /^('|"|&quot;|&apos;)(.+)('|"|&quot;|&apos;)$/
        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

[Source]

# 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

[Source]

# 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

[Source]

# 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

[Source]

# 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.

[Source]

# 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

[Source]

# 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

[Source]

# 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

[Source]

# File lib/xtemplate/xpath.rb, line 570
    def value_p(val)
      puts(value_inspect(val))
    end

[Source]

# 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

[Source]

# File lib/xtemplate/xpath.rb, line 709
    def xpath(path, data, plugin=nil)
      plugin ||= Action.new
      x = value_by_path2(path, data, nil, data, plugin)
      if( x.nil? )
        nil
      else
        case x
        when Array
          x
        else
          [x]
        end
      end
    end

[Validate]