scrubyt 0.1.0 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,3 +7,4 @@ require 'scrubyt/pattern.rb'
7
7
  require 'scrubyt/result_dumper.rb'
8
8
  require 'scrubyt/result.rb'
9
9
  require 'scrubyt/xpathutils.rb'
10
+ require 'scrubyt/post_processor.rb'
@@ -36,12 +36,11 @@ module Scrubyt
36
36
  #2b) Do it on the XML level - most probably this solution will be implemented
37
37
 
38
38
  # Different constraint types
39
- CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ANCESTOR_PATTERN = 0
40
- CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ANCESTOR_PATTERN = 1
41
- CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ATTRIBUTE = 2
42
- CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ATTRIBUTE = 3
43
- CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ANCESTOR_NODE = 4
44
- CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ANCESTOR_NODE = 5
39
+ CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_PATTERN = 0
40
+ CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ATTRIBUTE = 1
41
+ CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ATTRIBUTE = 2
42
+ CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ANCESTOR_NODE = 3
43
+ CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ANCESTOR_NODE = 4
45
44
 
46
45
 
47
46
  attr_reader :type, :target, :parent_filter
@@ -52,22 +51,12 @@ module Scrubyt
52
51
  #(child pattern, or child pattern of a child pattern, etc.) denoted by "ancestor"
53
52
  #'Has an ancestor pattern' means that the ancestor pattern actually extracts something
54
53
  #(just by looking at the wrapper model, the ancestor pattern is always present)
55
- #ON result level!!!
56
- def self.add_ensure_presence_of_ancestor_pattern(parent_filter, ancestor)
57
- Constraint.new(parent_filter, ancestor, CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ANCESTOR_PATTERN)
54
+ #Note that from this type of constraint there is no 'ensure_absence' version, since
55
+ #I could not think about an use case for that
56
+ def self.add_ensure_presence_of_pattern(parent_filter, ancestor)
57
+ Constraint.new(parent_filter, ancestor, CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_PATTERN)
58
58
  end
59
59
 
60
- #Add 'ensure presence of ancestor pattern' constraint
61
-
62
- #If this type of constraint is added to a pattern, it must NOT have an ancestor pattern
63
- #(child pattern, or child pattern of a child pattern, etc.) denoted by "ancestor"
64
- #'Has an ancestor pattern' means that the ancestor pattern actually extracts something
65
- #(just by looking at the wrapper model, the ancestor pattern is always present)
66
- #ON result level!!!
67
- def self.add_ensure_absence_of_ancestor_pattern(parent_filter, ancestor)
68
- Constraint.new(parent_filter, ancestor, CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ANCESTOR_PATTERN)
69
- end
70
-
71
60
  #Add 'ensure absence of attribute' constraint
72
61
 
73
62
  #If this type of constraint is added to a pattern, the HTML node it targets
@@ -127,10 +116,9 @@ module Scrubyt
127
116
  #content of the pattern
128
117
  def check(result)
129
118
  case @type
130
- when CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ANCESTOR_PATTERN
131
- puts "CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ANCESTOR_PATTERN"
132
- when CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ANCESTOR_PATTERN
133
- puts "CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ANCESTOR_PATTERN"
119
+ #checked after evaluation, so here always return true
120
+ when CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_PATTERN
121
+ return true
134
122
  when CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_ATTRIBUTE
135
123
  attribute_present(result)
136
124
  when CONSTRAINT_TYPE_ENSURE_ABSENCE_OF_ATTRIBUTE
@@ -11,15 +11,8 @@ module Scrubyt
11
11
  #functions with their documentation in Scrubyt::Constraint.rb
12
12
  class ConstraintAdder
13
13
 
14
- def self.ensure_presence_of_ancestor_pattern(pattern, ancestor_node_name)
15
- data = self.prepare_ensure_ancestor_pattern(pattern, sym_root, sym_ancestor)
16
- pattern.filters[0].ensure_presence_of_ancestor_pattern(ancestor_node_name)
17
- pattern #To make chaining possible
18
- end
19
-
20
- def self.ensure_absence_of_ancestor_pattern(pattern, ancestor_node_name)
21
- data = self.prepare_ensure_ancestor_pattern(pattern, sym_root, sym_ancestor)
22
- pattern.filters[0].ensure_absence_of_ancestor_pattern(ancestor_node_name)
14
+ def self.ensure_presence_of_pattern(pattern, ancestor_node_name)
15
+ pattern.filters[0].ensure_presence_of_pattern(ancestor_node_name)
23
16
  pattern #To make chaining possible
24
17
  end
25
18
 
@@ -74,13 +67,6 @@ private
74
67
  end
75
68
  end
76
69
  return attribute_pairs
77
- end
78
-
79
- def self.prepare_ensure_ancestor_pattern(pattern, root, ancestor)
80
- context_pattern = find_by_name(pattern.root_pattern, root)
81
- target_pattern = find_by_name(pattern.root_pattern, ancestor)
82
- return [context_pattern, target_pattern]
83
- end
84
-
70
+ end #end of method prepare_attributes
85
71
  end #end of class ConstraintAddere
86
72
  end #end of module Scrubyt
@@ -80,7 +80,7 @@ module Scrubyt
80
80
 
81
81
  private
82
82
  def self.export_header(output_file)
83
- @result += "require 'lib/extractor.rb'\n\n"
83
+ @result += "require 'rubygems'\nrequire 'scrubyt'\n\n"
84
84
  end
85
85
 
86
86
  def self.cleanup_result
@@ -142,19 +142,21 @@ private
142
142
  @name_to_xpath_map = {}
143
143
  create_name_to_xpath_map(pattern)
144
144
  #Replace the examples which are quoted with " and '
145
- @name_to_xpath_map.each do |name, xpath|
146
- replace_example_with_xpath(name, xpath, %q{"})
147
- replace_example_with_xpath(name, xpath, %q{'})
145
+ @name_to_xpath_map.each do |name, xpaths|
146
+ replace_example_with_xpath(name, xpaths, %q{"})
147
+ replace_example_with_xpath(name, xpaths, %q{'})
148
148
  end
149
149
  #Finally, add XPaths to pattern which had no example at the beginning (the XPath was
150
150
  #generated from the child patterns
151
- @name_to_xpath_map.each do |name, xpath|
152
- comma = @full_definition.scan(Regexp.new("P.#{name}(.+)$"))[0][0].sub('do'){}.strip == '' ? '' : ','
153
- if (@full_definition.scan(Regexp.new("P.#{name}(.+)$"))[0][0]).include?('{')
154
- @full_definition.sub!("P.#{name}") {"P.#{name}('#{xpath}')"}
155
- else
156
- @full_definition.sub!("P.#{name}") {"P.#{name} \"#{xpath}\"#{comma}"}
157
- end
151
+ @name_to_xpath_map.each do |name, xpaths|
152
+ xpaths.each do |xpath|
153
+ comma = @full_definition.scan(Regexp.new("P.#{name}(.+)$"))[0][0].sub('do'){}.strip == '' ? '' : ','
154
+ if (@full_definition.scan(Regexp.new("P.#{name}(.+)$"))[0][0]).include?('{')
155
+ @full_definition.sub!("P.#{name}") {"P.#{name}('#{xpath}')"}
156
+ else
157
+ @full_definition.sub!("P.#{name}") {"P.#{name} \"#{xpath}\"#{comma}"}
158
+ end
159
+ end
158
160
  end
159
161
  @result += @full_definition
160
162
  end
@@ -169,18 +171,32 @@ private
169
171
 
170
172
 
171
173
  def self.create_name_to_xpath_map(pattern)
172
- @name_to_xpath_map[pattern.name] = pattern.filters[0].xpath if pattern.filters[0].xpath != nil
174
+ @name_to_xpath_map[pattern.name] = []
175
+ pattern.filters.each do |filter|
176
+ @name_to_xpath_map[pattern.name] << filter.xpath if pattern.filters[0].xpath != nil
177
+ end
173
178
  pattern.children.each {|child| create_name_to_xpath_map child}
174
179
  end
175
180
 
176
- def self.replace_example_with_xpath(name, xpath, left_delimiter, right_delimiter=left_delimiter)
177
- replacing_xpath = (@full_definition.scan(Regexp.new("P.#{name}(.+)$"))[0][0]).include?('{') ?
178
- "P.#{name}('\"#{xpath}\"')" :
179
- "P.#{name} \"#{xpath}\""
181
+ def self.replace_example_with_xpath(name, xpaths, left_delimiter, right_delimiter=left_delimiter)
182
+ return if name=='root'
183
+ full_line = @full_definition.scan(Regexp.new("P.#{name}(.+)$"))[0][0]
184
+ examples = full_line.split(",")
185
+ examples.reject! {|exa| exa.strip!; exa[0..0] != %q{"} && exa[0..0] != %q{'} }
186
+ all_xpaths = ""
187
+ examples.each do |e|
188
+ index = examples.index(e)
189
+ xpath = xpaths[index]
190
+ return if xpath == nil
191
+ all_xpaths += ", " if index > 0
192
+ all_xpaths += '"' + xpath + '"'
193
+ end
194
+ replacing_xpath = full_line.include?('{') ? "P.#{name}('#{all_xpaths}')" :
195
+ "P.#{name} #{all_xpaths}"
180
196
  @full_definition.sub!(/P\.#{name}\s+#{left_delimiter}(.*)#{right_delimiter}/) do
181
197
  @name_to_xpath_map.delete("#{name}")
182
198
  replacing_xpath
183
- end
199
+ end
184
200
  end
185
201
 
186
202
  end
@@ -4,6 +4,7 @@ require 'rubygems'
4
4
  require 'mechanize'
5
5
  require 'hpricot'
6
6
  require 'pp'
7
+ require 'set'
7
8
 
8
9
  module Scrubyt
9
10
  ##
@@ -43,6 +44,8 @@ module Scrubyt
43
44
  else
44
45
  evaluate_wrapper(root_pattern)
45
46
  end
47
+ ensure_all_postconditions(root_pattern)
48
+ PostProcessor.remove_multiple_filter_duplicates(root_pattern)
46
49
  #Return the root pattern
47
50
  root_pattern
48
51
  end
@@ -104,39 +107,35 @@ module Scrubyt
104
107
 
105
108
  ##
106
109
  #Action to fetch a document (either a file or a http address)
107
- #
110
+ #
108
111
  #*parameters*
109
112
  #
110
113
  #_doc_url_ - the url or file name to fetch
111
114
  def self.fetch(doc_url, mechanize_doc=nil)
112
- puts "fetching: #{doc_url}"
113
115
  if (mechanize_doc == nil)
114
116
  @@current_doc_url = doc_url
115
117
  @@current_doc_protocol = ((doc_url =~ /^http/ || doc_url =~ /^www/) ? :http : :file)
116
118
  if @@base_dir == nil
117
119
  @@base_dir = doc_url.scan(/.+\//)[0] if @@current_doc_protocol == :file
118
- else
120
+ else
119
121
  @@current_doc_url = ((@@base_dir + doc_url) if doc_url !~ /#{@@base_dir}/)
120
122
  end
121
123
 
122
- if @@host_name == nil
123
- if @@current_doc_protocol == :http
124
- @@host_name = doc_url.scan(/http:\/\/.+?\//)[0]
125
- @@host_name = doc_url if @@host_name == nil
126
- end
127
- else
128
- @@current_doc_url = (@@host_name + doc_url) if doc_url !~ /#{@@host_name}/
124
+ if @@host_name != nil
125
+ if doc_url !~ /#{@@host_name}/
126
+ @@current_doc_url = (@@host_name + doc_url)
127
+ @@current_doc_url.gsub!(/([^:])\/\//) {"#{$1}/"}
128
+ end
129
129
  end
130
-
130
+ puts "[ACTION] fetching document: #{@@current_doc_url}"
131
131
  @@mechanize_doc = @@agent.get(@@current_doc_url) if @@current_doc_protocol == :http
132
132
  else
133
133
  @@current_doc_url = doc_url
134
134
  @@mechanize_doc = mechanize_doc
135
+ @@host_name = 'http://' + @@mechanize_doc.uri.to_s.scan(/http:\/\/(.+\/)+/).flatten[0]
136
+ @@host_name = doc_url if @@host_name == nil
135
137
  end
136
- @@hpricot_doc = mechanize_doc != nil ? Hpricot(@@mechanize_doc.body) : Hpricot(open(@@current_doc_url))
137
- out = open('kamaty.html', 'w')
138
- out.write @@hpricot_doc.to_s
139
- out.close
138
+ @@hpricot_doc = Hpricot(open(@@current_doc_url))#.to_original_html
140
139
  end
141
140
 
142
141
  ##
@@ -149,7 +148,7 @@ module Scrubyt
149
148
  #
150
149
  #_query_string_ - the string that should be entered into the textfield
151
150
  def self.fill_textfield(textfield_name, query_string)
152
- puts 'fill textfield'
151
+ puts "[ACTION] typing #{query_string} into the textfield named '#{textfield_name}'"
153
152
  textfield = (@@hpricot_doc/"input[@name=#{textfield_name}]").map()[0]
154
153
  formname = Scrubyt::XPathUtils.traverse_up_until_name(textfield, 'form').attributes['name']
155
154
  @@current_form = @@mechanize_doc.forms.with.name(formname).first
@@ -158,16 +157,16 @@ module Scrubyt
158
157
 
159
158
  #Submit the last form;
160
159
  def self.submit
161
- puts 'submit'
160
+ puts '[ACTION] submitting form...'
162
161
  result_page = @@agent.submit(@@current_form)#, @@current_form.buttons.first)
163
162
  @@current_doc_url = result_page.uri.to_s
164
163
  fetch(@@current_doc_url, result_page)
165
164
  end
166
165
 
167
- def self.click_link(link_text)
168
- puts 'click link'
169
- puts /^#{Regexp.escape(link_text)}$/
170
- p /^#{Regexp.escape(link_text)}$/
166
+ def self.click_link(link_text)
167
+ puts "[ACTION] clicking link: #{link_text}"
168
+ #puts /^#{Regexp.escape(link_text)}$/
169
+ #p /^#{Regexp.escape(link_text)}$/
171
170
  link = @@mechanize_doc.links.text(/^#{Regexp.escape(link_text)}$/)
172
171
  result_page = @@agent.click(link)
173
172
  @@current_doc_url = result_page.uri.to_s
@@ -178,10 +177,62 @@ module Scrubyt
178
177
  #############
179
178
 
180
179
  private
180
+ def self.ensure_all_postconditions(pattern)
181
+ ensure_postconditions(pattern)
182
+ pattern.children.each {|child| ensure_all_postconditions(child)}
183
+ end
184
+
185
+ def self.ensure_postconditions(pattern)
186
+ #holds the name of those child patterns which have to be present as children of the input parameter
187
+ epop_names = pattern.get_constraints.select {|c| c.type == Scrubyt::Constraint::CONSTRAINT_TYPE_ENSURE_PRESENCE_OF_PATTERN}.map {|c| c.target}
188
+ return if epop_names.empty?
189
+ #all_parent_values holds instances extracted by pattern
190
+ all_parent_values = []
191
+ pattern.result.childmap.each { |h| all_parent_values << h.values }
192
+ all_parent_values.flatten!
193
+ #indices of result instances (of pattern) we are going to remove
194
+ results_to_remove = Set.new
195
+ pattern.children.each do |child_pattern|
196
+ #all_child_values holds instances extracted by child_pattern
197
+ all_child_values = []
198
+ child_pattern.result.childmap.each { |h| all_child_values << h.values }
199
+ all_child_values.flatten!
200
+
201
+ #populate results_to_remove
202
+ i = 0
203
+ all_parent_values.each do |parent_value|
204
+ #Hey! Not just the direct children but all the ancestors
205
+ @found_ancestor = false
206
+ check_ancestors(parent_value, all_child_values)
207
+
208
+ results_to_remove << i if (!@found_ancestor && (epop_names.include? child_pattern.name))
209
+ i += 1
210
+ end
211
+ end
212
+ #based on results_to_remove, populate the array 'rejected' which holds the actual instances
213
+ #(and not indices, as in the case of results_to_remove!). In other words, we are mapping
214
+ #results_to_remove indices to their actual instances
215
+ rejected = []
216
+ i = -1
217
+ pattern.result.childmap.each do |h|
218
+ h.each { |k,v| rejected = v.reject {|e| i += 1; !results_to_remove.include? i } }
219
+ end
220
+
221
+ #Correct the statistics
222
+ pattern.get_instance_count[pattern.name] -= rejected.size
223
+
224
+ #Finally, do the actual delete!
225
+ pattern.result.childmap.each { |h| h.each { |k,v| rejected.each { |r| v.delete(r)} } }
226
+ end
227
+
228
+ def self.check_ancestors(parent_value, all_child_values)
229
+ parent_value.children.each { |child| @found_ancestor = true if all_child_values.include? child }
230
+ parent_value.children.each { |child| check_ancestors(child, all_child_values) if child.is_a? Hpricot::Elem }
231
+ end
232
+
181
233
  def self.evaluate_wrapper(pattern)
182
234
  pattern.evaluate
183
235
  pattern.children.each { |child| evaluate_wrapper child }
184
- end
185
-
236
+ end #end of method evaluate_wrapper
186
237
  end #end of class Extractor
187
238
  end #end of module Scrubyt
@@ -2,7 +2,7 @@ module Scrubyt
2
2
  ##
3
3
  #=<tt>Filter out relevant pieces from the parent pattern</tt>
4
4
  #
5
- #A Scrubyt wrapper is almost like a waterfall: water is pouring from the top until
5
+ #A Scrubyt extractor is almost like a waterfall: water is pouring from the top until
6
6
  #it reaches the bottom. The biggest difference is that instead of water, a HTML
7
7
  #document travels through the space.
8
8
  #
@@ -15,12 +15,12 @@ module Scrubyt
15
15
  #The working of a filter will be explained most easily by the help of an example.
16
16
  #Let's consider that we would like to extract information from a webshop; Concretely
17
17
  #we are interested in the name of the items and the URL pointing to the image of the
18
- #item
18
+ #item.
19
19
  #
20
- #To accomplish this. first we select the items with the pattern item (a pattern is
20
+ #To accomplish this, first we select the items with the pattern item (a pattern is
21
21
  #a logical grouping of fillters; see Pattern documentation) Then our new
22
- #context is the result extracted by the item pattern; For every pattern, further
23
- #extract the name and the image of the item; and finally, extractr the href attribute
22
+ #context is the result extracted by the 'item' pattern; For every 'item' pattern, further
23
+ #extract the name and the image of the item; and finally, extract the href attribute
24
24
  #of the image. Let's see an illustration:
25
25
  #
26
26
  # root --> This pattern is called a 'root pattern', It is invisible to you
@@ -46,15 +46,18 @@ module Scrubyt
46
46
  #Regexp example, like /\d+@*\d+[a-z]/
47
47
  EXAMPLE_TYPE_REGEXP = 4
48
48
 
49
- attr_accessor :example_type, :parent_pattern, :temp_sink, :constraints, :xpath, :regexp
49
+ attr_accessor :example_type, :parent_pattern, :temp_sink,
50
+ :constraints, :xpath, :regexp, :example, :source, :sink
50
51
 
51
- def initialize(parent_pattern, *args)
52
+ def initialize(parent_pattern, example=nil, *args)
52
53
  @parent_pattern = parent_pattern
53
54
  #If the example type is not explicitly defined in the pattern definition,
54
55
  #try to determine it automatically from the example
55
- @example_type = (args[0] == nil ? Filter.determine_example_type(parent_pattern.example) :
56
+ @example_type = (args[0] == nil ? Filter.determine_example_type(example) :
56
57
  args[0][:example_type])
57
- @regexp = parent_pattern.example if @example_type == EXAMPLE_TYPE_REGEXP
58
+ @sink = [] #output of a filter
59
+ @source = [] #input of a filter
60
+ @example = example
58
61
  @xpath = nil #The xpath to evaluate this filter
59
62
  #temp sinks are used for the initial run when determining the XPaths for examples;
60
63
  @temp_sink = nil
@@ -64,14 +67,15 @@ module Scrubyt
64
67
  #Evaluate this filter. This method shoulf not be called directly - as the pattern hierarchy
65
68
  #is evaluated, every pattern evaluates its filters and then they are calling this method
66
69
  def evaluate(source)
70
+ @parent_pattern.root_pattern.already_evaluated_sources ||= {}
67
71
  case @parent_pattern.type
68
- when Scrubyt::Pattern::PATTERN_TYPE_TREE
72
+ when Scrubyt::Pattern::PATTERN_TYPE_TREE
69
73
  result = source/@xpath
70
74
  result.class == Hpricot::Elements ? result.map : [result]
71
75
  when Scrubyt::Pattern::PATTERN_TYPE_ATTRIBUTE
72
- [source.attributes[@parent_pattern.example]]
76
+ [source.attributes[@example]]
73
77
  when Scrubyt::Pattern::PATTERN_TYPE_REGEXP
74
- source.inner_text.scan(@regexp).flatten
78
+ source.inner_text.scan(@example).flatten
75
79
  end
76
80
  end
77
81
 
@@ -81,38 +85,49 @@ module Scrubyt
81
85
  def generate_XPath_for_example
82
86
  case @example_type
83
87
  when EXAMPLE_TYPE_XPATH
84
- @xpath = @parent_pattern.example
88
+ @xpath = @example
85
89
  when EXAMPLE_TYPE_STRING
86
- @temp_sink = XPathUtils.find_node_from_text( @parent_pattern.root_pattern.source[0], @parent_pattern.example )
90
+ @temp_sink = XPathUtils.find_node_from_text( @parent_pattern.root_pattern.filters[0].source[0], @example )
87
91
  @xpath = @parent_pattern.generalize ? XPathUtils.generate_XPath(@temp_sink, nil, false) :
88
92
  XPathUtils.generate_XPath(@temp_sink, nil, true)
89
- when EXAMPLE_TYPE_CHILDREN
90
- all_child_temp_sinks = []
91
- @parent_pattern.children.each do |child_pattern|
92
- child_pattern.filters.each do |filter|
93
- all_child_temp_sinks << filter.temp_sink
93
+ puts @xpath
94
+ when EXAMPLE_TYPE_CHILDREN
95
+ current_example_index = 0
96
+ loop do
97
+ all_child_temp_sinks = []
98
+ @parent_pattern.children.each do |child_pattern|
99
+ all_child_temp_sinks << child_pattern.filters[current_example_index].temp_sink
94
100
  end
95
- end
96
- result = all_child_temp_sinks.pop
97
- if all_child_temp_sinks.empty?
98
- result = result.parent
99
- else
100
- all_child_temp_sinks.each do |child_sink|
101
- result = XPathUtils.lowest_common_ancestor(result, child_sink)
101
+
102
+ result = all_child_temp_sinks.pop
103
+ if all_child_temp_sinks.empty?
104
+ result = result.parent
105
+ else
106
+ all_child_temp_sinks.each do |child_sink|
107
+ result = XPathUtils.lowest_common_ancestor(result, child_sink)
108
+ end
102
109
  end
110
+ xpath = @parent_pattern.generalize ? XPathUtils.generate_XPath(result, nil, false) :
111
+ XPathUtils.generate_XPath(result, nil, true)
112
+ if @parent_pattern.filters.size < current_example_index + 1
113
+ @parent_pattern.filters << Scrubyt::Filter.new(@parent_pattern)
114
+ end
115
+ @parent_pattern.filters[current_example_index].xpath = xpath
116
+ @parent_pattern.filters[current_example_index].temp_sink = result
117
+ @parent_pattern.children.each do |child_pattern|
118
+ child_pattern.filters[current_example_index].xpath =
119
+ child_pattern.generalize ? XPathUtils.generate_generalized_relative_XPath(child_pattern.filters[current_example_index].temp_sink, result) :
120
+ XPathUtils.generate_relative_XPath(child_pattern.filters[current_example_index].temp_sink, result)
121
+ end
122
+ if @parent_pattern.children[0].examples == nil
123
+ break if @parent_pattern.children[0].filters.size == current_example_index+1
124
+ else
125
+ break if @parent_pattern.children[0].examples.size == current_example_index+1
126
+ end
127
+ current_example_index += 1
103
128
  end
104
- @temp_sink = result
105
- @xpath = @parent_pattern.generalize ? XPathUtils.generate_XPath(@temp_sink, nil, false) :
106
- XPathUtils.generate_XPath(@temp_sink, nil, true)
107
- @parent_pattern.children.each do |child_pattern|
108
- child_pattern.filters.each do |filter|
109
- filter.xpath =
110
- child_pattern.generalize ? XPathUtils.generate_generalized_relative_XPath(filter.temp_sink, result) :
111
- XPathUtils.generate_relative_XPath(filter.temp_sink, result)
112
- end
113
- end
114
129
  when EXAMPLE_TYPE_IMAGE
115
- @temp_sink = XPathUtils.find_image(@parent_pattern.root_pattern.source[0], @parent_pattern.example)
130
+ @temp_sink = XPathUtils.find_image(@parent_pattern.root_pattern.filters[0].source[0], @example)
116
131
  @xpath = XPathUtils.generate_XPath(@temp_sink, nil, false)
117
132
  end
118
133
  end