Class: VCDOM::XPath::Internal::Evaluator

Inherits:
Object
  • Object
show all
Defined in:
lib/vcdom/xpath/internal/evaluator.rb

Overview

:nodoc:

Constant Summary collapse

NODE_SELECTION_BY_AXIS_PROCS =
{
  :'self'  => lambda { |c_node, new_nodes|
    new_nodes << c_node
  },
  :'child' => lambda { |c_node, new_nodes|
    c_node.each_child_node { |n| new_nodes << n }
  },
  :'parent' => lambda { |c_node, new_nodes|
    if c_node.node_type == c_node.class::ATTRIBUTE_NODE then # TODO
      new_nodes << c_node.owner_element if c_node.owner_element
    elsif c_node.node_type == :xpath_namespace_node then
      raise "NOT SUPPORTED"
    else
      new_nodes << c_node.parent_node if c_node.parent_node
    end
  },
  :'descendant' => lambda { |c_node, new_nodes|
    node = c_node.first_child
    if node then
      while not node.equal? c_node do
        new_nodes << node
        if node.first_child then
          node = node.first_child
        elsif node.next_sibling
          node = node.next_sibling
        else
          # TODO
          node = node.parent_node
          while not node.equal? c_node do
            if node.next_sibling then
              node = node.next_sibling
              break
            else
              node = node.parent_node
            end
          end
        end
      end
    end
  },
  :'descendant-or-self' => lambda { |c_node, new_nodes|
    new_nodes << c_node
    NODE_SELECTION_BY_AXIS_PROCS[:"descendant"].call( c_node, new_nodes )
  },
  :'ancestor' => lambda { |c_node, new_nodes|
    node = c_node.parent_node
    while node do
      new_nodes << node
      node = node.parent_node
    end
  },
  :'ancestor-or-self' => lambda { |c_node, new_nodes|
    new_nodes << c_node
    NODE_SELECTION_BY_AXIS_PROCS[:"ancestor"].call( c_node, new_nodes )
  },
  :'following' => lambda { |c_node, new_nodes|
    raise "NOT SUPPORTED"
  },
  :'following-sibling' => lambda { |c_node, new_nodes|
    raise "NOT SUPPORTED"
  },
  :'preceding' => lambda { |c_node, new_nodes|
    raise "NOT SUPPORTED"
  },
  :'preceding-sibling' => lambda { |c_node, new_nodes|
    raise "NOT SUPPORTED"
  },
  :'attribute' => lambda { |c_node, new_nodes|
    if c_node.attributes then
      c_node.attributes.each do |n|
        if n.namespace_uri != "http://www.w3.org/2000/xmlns/" then
          new_nodes << n
        end
      end
    end
  },
  :'namespace' => lambda { |c_node, new_nodes|
    raise "NOT SUPPORTED"
  },
}

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context_node) ⇒ Evaluator

Returns a new instance of Evaluator.



287
288
289
290
291
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 287

def initialize( context_node )
  ( @context_node_stack = Array.new() ).push context_node
  ( @context_size_stack = Array.new() ).push 1
  ( @context_pos_stack  = Array.new() ).push 1
end

Class Method Details

.set_xpath_function(name, func) ⇒ Object



293
294
295
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 293

def self.set_xpath_function( name, func )
  define_method "_xpath_#{name}".intern, func
end

Instance Method Details

#call_function(name, args) ⇒ Object



94
95
96
97
98
99
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 94

def call_function( name, args )
  args.each do |a|
    raise "USER ERROR" unless a.is_value?
  end
  send( "_xpath_#{name}".intern, *args )
end

#change_context_node_and_position(context_node, position, &block) ⇒ Object



13
14
15
16
17
18
19
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 13

def change_context_node_and_position( context_node, position, &block )
  @context_node_stack.push context_node
  @context_pos_stack.push position
  block.call
  @context_pos_stack.pop
  @context_node_stack.pop
end

#change_context_size(size, &block) ⇒ Object



20
21
22
23
24
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 20

def change_context_size( size, &block )
  @context_size_stack.push size
  block.call
  @context_size_stack.pop
end

#context_nodeObject



9
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 9

def context_node;     @context_node_stack[-1] end

#context_positionObject



11
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 11

def context_position; @context_pos_stack[-1]  end

#context_sizeObject



10
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 10

def context_size;     @context_size_stack[-1] end

#create_boolean_value(bool) ⇒ Object



277
278
279
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 277

def create_boolean_value( bool )
  bool ? BooleanValue::TRUE : BooleanValue::FALSE
end

#create_node_set_value(*nodes) ⇒ Object



283
284
285
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 283

def create_node_set_value( *nodes )
  NodeSetValue.new( *nodes )
end

#create_number_value(num) ⇒ Object



274
275
276
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 274

def create_number_value( num )
  NumberValue.new( num.to_f )
end

#create_string_value(str) ⇒ Object



280
281
282
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 280

def create_string_value( str )
  StringValue.new( str )
end

#evaluate_and_expr(expr) ⇒ Object



45
46
47
48
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 45

def evaluate_and_expr( expr )
  # TODO
  raise "NOT STILL BE SUPPORTED"
end

#evaluate_equality_expr(expr) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 50

def evaluate_equality_expr( expr )
  # TODO
  val_stack = Array.new()
  expr.each do |e|
    if e.is_value? or e.is_expr? then
      val_stack.push e
    else
      case e.command_type
      when :operation_unary
        operand = val_stack.pop
        val_stack.push operand.send( e.operation_name )
      when :operation_binary
        operand2 = val_stack.pop
        operand1 = val_stack.pop
        val_stack.push operand1.send( e.operation_name, operand2 )
      when :expr_eval
        val_stack.push evaluate_expr( e.expr )
      when :function_call
        args = Array.new()
        e.function_args.each do |arg_expr|
          args << evaluate_expr( arg_expr )
        end
        val_stack.push( call_function( e.function_name, args ) )
      when :node_selection
        target = val_stack.pop
        val_stack.push evaluate_node_selection( target, e )
      when :preds_eval
        target = val_stack.pop
        val_stack.push evaluate_preds( target, e.exprs )
      when :context_node
        val_stack.push create_node_set_value( context_node )
      when :root_node
        # TODO : context_node.class::DOCUMENT_NODE を書き換え
        root_node = ( context_node.node_type == context_node.class::DOCUMENT_NODE ? context_node : context_node.owner_document )
        val_stack.push create_node_set_value( root_node )
      else
        raise "NOT SUPPORTED"
      end
    end
  end
  raise "INTERNAL ERROR" if val_stack.length != 1
  return val_stack.pop
end

#evaluate_expr(expr) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 26

def evaluate_expr( expr )
  # TODO
  case expr.expr_type
  when :or_expr
    return evaluate_or_expr( expr )
  when :and_expr
    return evaluate_and_expr( expr )
  when :equality_expr
    return evaluate_equality_expr( expr )
  else
    raise "INTERNAL ERROR"
  end
end

#evaluate_node_selection(nodes, cmd) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 182

def evaluate_node_selection( nodes, cmd )
  # cmd.pred_exprs は nil の可能性あり
  # cmd.node_test_name も nil の可能性あり
  # cmd.node_test_type は :any, :, :processing-instruction など
  new_nodes = Array.new()
  nodes.each do |c_node|
    c_nodes = Array.new()
    # TODO : 軸によって対象となるノードを集める
    if ( node_selection_by_axis_proc = NODE_SELECTION_BY_AXIS_PROCS[ cmd.axis ] ).nil? then
      raise "invalid axis [#{cmd.axis}]"
    end
    node_selection_by_axis_proc.call( c_node, c_nodes )
    # type, name, ns url によって絞り込む
    c_nodes = evaluate_node_selection_sub( c_nodes, cmd )
    # TODO : 軸によって順序を変更?
    if cmd.pred_exprs then
      cmd.pred_exprs.each do |expr|
        c_nodes = evaluate_pred_internal( c_nodes, expr )
      end
    end
    new_nodes.concat c_nodes
  end
  new_nodes.uniq!
  create_node_set_value( *new_nodes )
end

#evaluate_node_selection_sub(nodes, cmd) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 208

def evaluate_node_selection_sub( nodes, cmd )
  # node_type での絞り込み
  case cmd.node_type
  when :node
    # do nothing
  when :named_node
    case cmd.axis
    when :attribute
      raise "NOT SUPPORTED"
    when :namespace
      raise "NOT SUPPORTED"
    else
      expected_type = VCDOM::Node::ELEMENT_NODE # TODO : change
    end
    nodes = nodes.inject( Array.new() ) do |arr,n|
      arr << n if n.node_type == expected_type
    end
  else
    raise "NOT SUPPORTED"
  end
  new_nodes = Array.new()
  # node_ns_url での絞り込み      # node_name での絞り込み
  if cmd.node_ns_uri.nil? then #and cmd.node_name.nil? then
    if cmd.node_name.nil? then
      new_nodes = nodes
    else
      nodes.each do |n|
        new_nodes << n if n.namespace_uri.nil? and ( n.local_name || n.node_name ) == cmd.node_name.to_s
      end
    end
  else
    nodes.each do |n|
      new_nodes << n if n.namespace_uri == cmd.node_ns_uri.to_s and n.local_name == cmd.node_name.to_s
    end
  end
  new_nodes
end

#evaluate_or_expr(expr) ⇒ Object



40
41
42
43
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 40

def evaluate_or_expr( expr )
  # TODO
  raise "NOT STILL BE SUPPORTED"
end

#evaluate_pred_internal(nodes, expr) ⇒ Object

evaluate_preds および evaluate_node_selection から呼び出される



258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 258

def evaluate_pred_internal( nodes, expr )
  new_nodes = Array.new()
  change_context_size( nodes.length ) do
    nodes.each_with_index do |node,i|
      change_context_node_and_position( node, i+1 ) do
        res = evaluate_expr( expr )
        if ( res.value_type == :number and res.value == context_position ) or
              ( res.value_type != :number and res.to_boolean_value.value ) then
          new_nodes << node
        end
      end
    end
  end
  new_nodes
end

#evaluate_preds(nodes, exprs) ⇒ Object



246
247
248
249
250
251
252
253
254
255
# File 'lib/vcdom/xpath/internal/evaluator.rb', line 246

def evaluate_preds( nodes, exprs )
  raise "User Error?" if nodes.value_type != :node_set
  # TODO
  nodes.sort()
  nodes = nodes.value
  exprs.each do |expr|
    nodes = evaluate_pred_internal( nodes, expr )
  end
  create_node_set_value( *nodes )
end