lightmodels 0.1.2-java → 0.2.1-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +8 -0
- data/Rakefile +9 -0
- data/lib/lightmodels/info_extraction.rb +133 -0
- data/lib/lightmodels/jsonser_nav.rb +12 -12
- data/lib/lightmodels/model_building.rb +66 -1
- data/lib/lightmodels/monkey_patching.rb +30 -0
- data/lib/lightmodels/parsing.rb +186 -0
- data/lib/lightmodels/query_serialized.rb +81 -0
- data/lib/lightmodels/rgen_ext.rb +153 -0
- data/lib/lightmodels/serialization.rb +11 -28
- data/lib/lightmodels/version.rb +3 -0
- data/lightmodels.gemspec +29 -0
- data/test/data/node_setCompleted.json +443 -0
- data/test/test_helper.rb +8 -0
- data/test/test_info_extraction.rb +90 -0
- data/test/test_queryserialized.rb +40 -0
- data/test/test_serialization.rb +64 -0
- metadata +122 -32
- checksums.yaml +0 -7
- data/lib/lightmodels/model.rb +0 -11
- data/lib/lightmodels/query.rb +0 -36
- data/lib/lightmodels/stats.rb +0 -69
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2013 ftomassetti
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
lightmodels
|
2
|
+
===========
|
3
|
+
|
4
|
+
lightmodels is a library to manipulate models and save them in a lightweight format (JSON is used for serialization).
|
5
|
+
|
6
|
+
EMF models and RGen models can be converted to lightmodels.
|
7
|
+
|
8
|
+
Other projects permit to obtain lightmodels representation of Ruby (ruby_to_emf_parser) and Java (java_model_jrb) code, Properties (properties_lightmodels) and XML (xml_lightmodels) files.
|
data/Rakefile
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
module LightModels
|
2
|
+
|
3
|
+
module InfoExtraction
|
4
|
+
|
5
|
+
class TermsBreaker
|
6
|
+
|
7
|
+
attr_accessor :sequences, :inv_sequences
|
8
|
+
|
9
|
+
def initialize(language_specific_logic)
|
10
|
+
@language_specific_logic = language_specific_logic
|
11
|
+
@sequences = Hash.new {|h,k|
|
12
|
+
h[k] = Hash.new {|h,k|
|
13
|
+
h[k]=0
|
14
|
+
}
|
15
|
+
}
|
16
|
+
@inv_sequences = Hash.new {|h,k|
|
17
|
+
h[k] = Hash.new {|h2,k2|
|
18
|
+
h2[k2]=0
|
19
|
+
}
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.from_context(language_specific_logic,context)
|
24
|
+
ser_context = LightModels::Serialization.jsonize_obj(context)
|
25
|
+
values_map = LightModels::QuerySerialized.collect_values_with_count(ser_context)
|
26
|
+
instance = new(language_specific_logic)
|
27
|
+
values_map.each do |value,c|
|
28
|
+
value = value.to_s.strip
|
29
|
+
if language_specific_logic.terms_containing_value?(value)
|
30
|
+
words = language_specific_logic.to_words(value)
|
31
|
+
first_words = words[0...-1]
|
32
|
+
instance.inv_sequences[words[0].downcase][:start] += c
|
33
|
+
first_words.each_with_index do |w,i|
|
34
|
+
instance.sequences[w.downcase][words[i+1].downcase] += c
|
35
|
+
instance.inv_sequences[words[i+1].downcase][w.downcase] += c
|
36
|
+
end
|
37
|
+
last_word = words.last
|
38
|
+
instance.sequences[last_word.downcase][:end] += c
|
39
|
+
else
|
40
|
+
# who cares, it will be never considered for composed names...
|
41
|
+
end
|
42
|
+
end
|
43
|
+
instance
|
44
|
+
end
|
45
|
+
|
46
|
+
def terms_in_value(value)
|
47
|
+
value = value.to_s.strip
|
48
|
+
if @language_specific_logic.terms_containing_value?(value)
|
49
|
+
words = @language_specific_logic.to_words(value)
|
50
|
+
group_words_in_terms(words).map{|w| w.downcase}
|
51
|
+
else
|
52
|
+
[value]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def frequent_straight_sequence?(w1,w2)
|
59
|
+
w1 = w1.downcase
|
60
|
+
w2 = w2.downcase
|
61
|
+
all_sequences_of_w1 = 0
|
62
|
+
@sequences[w1].each do |k,v|
|
63
|
+
all_sequences_of_w1 += v
|
64
|
+
end
|
65
|
+
sequences_w1_w2 = @sequences[w1][w2]
|
66
|
+
(sequences_w1_w2.to_f/all_sequences_of_w1.to_f)>0.5
|
67
|
+
end
|
68
|
+
|
69
|
+
def frequent_inverse_sequence?(w1,w2)
|
70
|
+
w1 = w1.downcase
|
71
|
+
w2 = w2.downcase
|
72
|
+
all_inv_sequences_of_w1 = 0
|
73
|
+
@inv_sequences[w1].each do |k,v|
|
74
|
+
all_inv_sequences_of_w1 += v
|
75
|
+
end
|
76
|
+
inv_sequences_w1_w2 = @inv_sequences[w1][w2]
|
77
|
+
(inv_sequences_w1_w2.to_f/all_inv_sequences_of_w1.to_f)>0.5
|
78
|
+
end
|
79
|
+
|
80
|
+
def frequent_sequence?(w1,w2)
|
81
|
+
return false unless w2
|
82
|
+
frequent_straight_sequence?(w1,w2) && frequent_inverse_sequence?(w2,w1)
|
83
|
+
end
|
84
|
+
|
85
|
+
def group_words_in_terms(words)
|
86
|
+
# getNotSoGoodFieldName is not a term because
|
87
|
+
# notSoGoodFieldName is more frequently alone that preceded by get
|
88
|
+
|
89
|
+
return words if words.count==1
|
90
|
+
start_term = 0
|
91
|
+
end_term = 0
|
92
|
+
term = words[0]
|
93
|
+
while end_term < words.count && frequent_sequence?(words[end_term],words[end_term+1])
|
94
|
+
end_term += 1
|
95
|
+
term = @language_specific_logic.concat(term,words[end_term])
|
96
|
+
end
|
97
|
+
return [term] if end_term==(words.count-1)
|
98
|
+
[term] + group_words_in_terms(words[(end_term+1)..-1])
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.values_map(model_node)
|
104
|
+
ser_model_node = LightModels::Serialization.jsonize_obj(model_node)
|
105
|
+
LightModels::QuerySerialized.collect_values_with_count(ser_model_node)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.terms_map(language_specific_logic,model_node,context=nil)
|
109
|
+
# context default to root
|
110
|
+
unless context
|
111
|
+
context = model_node
|
112
|
+
while context.eContainer
|
113
|
+
context = context.eContainer
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# look into context to see how frequent are certain series of words,
|
118
|
+
# frequent series are recognized as composed terms
|
119
|
+
terms_breaker = TermsBreaker.from_context(language_specific_logic,context)
|
120
|
+
|
121
|
+
values_map = values_map(model_node)
|
122
|
+
terms_map = Hash.new {|h,k| h[k]=0}
|
123
|
+
values_map.each do |v,n|
|
124
|
+
terms_breaker.terms_in_value(v).each do |t|
|
125
|
+
terms_map[t] += n
|
126
|
+
end
|
127
|
+
end
|
128
|
+
terms_map
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
@@ -3,20 +3,30 @@
|
|
3
3
|
|
4
4
|
module LightModels
|
5
5
|
|
6
|
-
module
|
6
|
+
module QuerySerialized
|
7
7
|
|
8
8
|
def self.rel_conts(root)
|
9
9
|
root.keys.select {|k| k.start_with? 'relcont_'}
|
10
10
|
end
|
11
11
|
|
12
12
|
def self.rel_non_conts(root)
|
13
|
-
root.keys.select {|k| k.start_with? '
|
13
|
+
root.keys.select {|k| k.start_with? 'relnoncont_'}
|
14
14
|
end
|
15
15
|
|
16
16
|
def self.attrs(root)
|
17
17
|
root.keys.select {|k| k.start_with? 'attr_'}
|
18
18
|
end
|
19
19
|
|
20
|
+
def self.print_tree(root,depth=0)
|
21
|
+
traverse(root) do |n,d|
|
22
|
+
s = ""
|
23
|
+
d.times { s = s + " " }
|
24
|
+
s = s + n['type'] if n
|
25
|
+
s = s + '<NIL>' unless n
|
26
|
+
puts s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
20
30
|
def self.values(root,feat)
|
21
31
|
raw = root[feat]
|
22
32
|
return [] if raw==nil
|
@@ -40,16 +50,6 @@ module Query
|
|
40
50
|
end
|
41
51
|
end
|
42
52
|
|
43
|
-
def self.print_tree(root,depth=0)
|
44
|
-
traverse(root) do |n,d|
|
45
|
-
s = ""
|
46
|
-
d.times { s = s + " " }
|
47
|
-
s = s + n['type'] if n
|
48
|
-
s = s + '<NIL>' unless n
|
49
|
-
puts s
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
53
|
end
|
54
54
|
|
55
55
|
end
|
@@ -8,6 +8,34 @@ class << self
|
|
8
8
|
attr_accessor :verbose
|
9
9
|
end
|
10
10
|
|
11
|
+
def self.handle_serialized_models_in_dir(src,src_extension,error_handler=nil,model_handler,&model_creator)
|
12
|
+
puts "== #{src} ==" if LightModels::ModelBuilding.verbose
|
13
|
+
Dir["#{src}/*"].each do |fd|
|
14
|
+
if File.directory? fd
|
15
|
+
basename = File.basename(fd)
|
16
|
+
handle_serialized_models_in_dir("#{src}/#{basename}",src_extension,error_handler,model_handler,&model_creator)
|
17
|
+
else
|
18
|
+
if File.extname(fd)==".#{src_extension}"
|
19
|
+
handle_serialized_model_per_file(fd,error_handler,model_handler,&model_creator)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.handle_models_in_dir(src,src_extension,error_handler=nil,model_handler,&model_creator)
|
26
|
+
puts "== #{src} ==" if LightModels::ModelBuilding.verbose
|
27
|
+
Dir["#{src}/*"].each do |fd|
|
28
|
+
if File.directory? fd
|
29
|
+
basename = File.basename(fd)
|
30
|
+
handle_models_in_dir("#{src}/#{basename}",src_extension,error_handler,model_handler,&model_creator)
|
31
|
+
else
|
32
|
+
if File.extname(fd)==".#{src_extension}"
|
33
|
+
handle_model_per_file(fd,error_handler,model_handler,&model_creator)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
11
39
|
def self.generate_models_in_dir(src,dest,src_extension,dest_extension,max_nesting=500,error_handler=nil,&model_creator)
|
12
40
|
puts "== #{src} -> #{dest} ==" if LightModels::ModelBuilding.verbose
|
13
41
|
Dir["#{src}/*"].each do |fd|
|
@@ -25,9 +53,46 @@ def self.generate_models_in_dir(src,dest,src_extension,dest_extension,max_nestin
|
|
25
53
|
end
|
26
54
|
end
|
27
55
|
|
56
|
+
def self.handle_model_per_file(src,error_handler=nil,model_handler,&models_generator)
|
57
|
+
puts "<Model from #{src}>" if LightModels::ModelBuilding.verbose
|
58
|
+
|
59
|
+
if error_handler
|
60
|
+
begin
|
61
|
+
m = models_generator.call(src)
|
62
|
+
model_handler.call(src,m)
|
63
|
+
rescue Exception => e
|
64
|
+
error_handler.call(src,e)
|
65
|
+
rescue
|
66
|
+
error_handler.call(src,nil)
|
67
|
+
end
|
68
|
+
else
|
69
|
+
m = models_generator.call(src)
|
70
|
+
model_handler.call(src,m)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.handle_serialized_model_per_file(src,error_handler=nil,model_handler,&models_generator)
|
75
|
+
puts "<Model from #{src}>" if LightModels::ModelBuilding.verbose
|
76
|
+
|
77
|
+
if error_handler
|
78
|
+
begin
|
79
|
+
m = models_generator.call(src)
|
80
|
+
model_handler.call(src,m)
|
81
|
+
rescue Exception => e
|
82
|
+
error_handler.call(src,e)
|
83
|
+
rescue
|
84
|
+
error_handler.call(src,nil)
|
85
|
+
end
|
86
|
+
else
|
87
|
+
m = models_generator.call(src)
|
88
|
+
model_handler.call(src,m)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
28
93
|
def self.generate_model_per_file(src,dest,max_nesting=500,error_handler=nil,&models_generator)
|
29
94
|
if not File.exist? dest
|
30
|
-
puts "<Model from #{src}>"
|
95
|
+
puts "<Model from #{src}>" if LightModels::ModelBuilding.verbose
|
31
96
|
|
32
97
|
if error_handler
|
33
98
|
begin
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
def simple_name
|
4
|
+
if (i = (r = name).rindex(':')) then r[0..i] = '' end
|
5
|
+
r
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class String
|
11
|
+
|
12
|
+
def remove_postfix(postfix)
|
13
|
+
raise "'#{self}'' have not the right postfix '#{postfix}'" unless end_with?(postfix)
|
14
|
+
self[0..-(1+postfix.length)]
|
15
|
+
end
|
16
|
+
|
17
|
+
def remove_prefix(prefix)
|
18
|
+
raise "'#{self}'' have not the right prefix '#{prefix}'" unless start_with?(prefix)
|
19
|
+
self[prefix.length..-1]
|
20
|
+
end
|
21
|
+
|
22
|
+
def proper_capitalize
|
23
|
+
self[0, 1].upcase + self[1..-1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def proper_uncapitalize
|
27
|
+
self[0, 1].downcase + self[1..-1]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'lightmodels/monkey_patching'
|
2
|
+
|
3
|
+
module LightModels
|
4
|
+
|
5
|
+
class ParsingError < Exception
|
6
|
+
attr_reader :node
|
7
|
+
attr_reader :line
|
8
|
+
|
9
|
+
def initialize(node,msg,line=nil)
|
10
|
+
@node = node
|
11
|
+
@msg = msg
|
12
|
+
@line = lin
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_s
|
16
|
+
"#{@msg}, start line: #{@line}"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class UnknownNodeType < ParsingError
|
22
|
+
|
23
|
+
def initialize(node,line=nil,node_type=nil,where=nil)
|
24
|
+
super(node,"UnknownNodeType: type=#{node_type} , where: #{where}")
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
module ParserWrapper
|
30
|
+
|
31
|
+
@@verbose = false
|
32
|
+
|
33
|
+
JavaCollection = ::Java::JavaClass.for_name("java.util.Collection")
|
34
|
+
|
35
|
+
def adapter(model_class,ref)
|
36
|
+
if adapter_specific_class(model_class,ref)
|
37
|
+
adapter_specific_class(model_class,ref)
|
38
|
+
else
|
39
|
+
if model_class.superclass!=Object
|
40
|
+
adapter(model_class.superclass,ref)
|
41
|
+
else
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def reference_to_method(model_class,ref)
|
48
|
+
s = ref.name
|
49
|
+
#s = 'value' if s=='body'
|
50
|
+
adapted = adapter(model_class,ref)
|
51
|
+
s = adapted if adapted
|
52
|
+
s.to_sym
|
53
|
+
end
|
54
|
+
|
55
|
+
def attribute_to_method(model_class,att)
|
56
|
+
s = att.name
|
57
|
+
adapted = adapter(model_class,att)
|
58
|
+
s = adapted if adapted
|
59
|
+
s.to_sym
|
60
|
+
end
|
61
|
+
|
62
|
+
def assign_ref_to_model(model,ref,value)
|
63
|
+
return unless value==nil # we do not need to assign a nil...
|
64
|
+
if ref.many
|
65
|
+
adder_method = :"add#{ref.name.capitalize}"
|
66
|
+
value.each {|el| model.send(adder_method,node_to_model(el))}
|
67
|
+
else
|
68
|
+
setter_method = :"#{ref.name}="
|
69
|
+
raise "Trying to assign an array to a single property. Class #{model.class}, property #{ref.name}" if value.is_a?(::Array)
|
70
|
+
model.send(setter_method,node_to_model(value))
|
71
|
+
end
|
72
|
+
rescue Object => e
|
73
|
+
puts "Problem while assigning ref #{ref.name} (many? #{ref.many}) to #{model.class}. Value: #{value.class}"
|
74
|
+
puts "\t<<#{e}>>"
|
75
|
+
raise e
|
76
|
+
end
|
77
|
+
|
78
|
+
def assign_att_to_model(model,att,value)
|
79
|
+
if att.many
|
80
|
+
adder_method = :"add#{att.name.capitalize}"
|
81
|
+
value.each {|el| model.send(adder_method,el)}
|
82
|
+
else
|
83
|
+
setter_method = :"#{att.name}="
|
84
|
+
raise "Trying to assign an array to a single property. Class #{model.class}, property #{att.name}" if value.is_a?(::Array)
|
85
|
+
model.send(setter_method,value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def populate_attr(node,att,model)
|
90
|
+
value = get_feature_value(node,att)
|
91
|
+
#puts "Value got for #{node.class} #{att} : #{value.class}"
|
92
|
+
# nil are ignored
|
93
|
+
model.send(:"#{att.name}=",value) if value!=nil
|
94
|
+
rescue Object => e
|
95
|
+
puts "Problem while populating attribute #{att.name} of #{model} from #{node}. Value: #{value}"
|
96
|
+
raise e
|
97
|
+
end
|
98
|
+
|
99
|
+
def populate_ref(node,ref,model)
|
100
|
+
log("populate ref #{ref.name}, node: #{node.class}, model: #{model.class}")
|
101
|
+
value = get_feature_value(node,ref)
|
102
|
+
log("\tvalue=#{value.class}")
|
103
|
+
if value!=nil
|
104
|
+
if value==node
|
105
|
+
puts "avoiding loop... #{ref.name}, class #{node.class}"
|
106
|
+
return
|
107
|
+
end
|
108
|
+
if JavaCollection.assignable_from?(value.java_class)
|
109
|
+
log("\tvalue is a collection")
|
110
|
+
capitalized_name = ref.name.proper_capitalize
|
111
|
+
value.each do |el|
|
112
|
+
unless el.respond_to?(:parent)
|
113
|
+
class << el
|
114
|
+
attr_accessor :parent
|
115
|
+
end
|
116
|
+
end
|
117
|
+
el.parent = node
|
118
|
+
model.send(:"add#{capitalized_name}",node_to_model(el))
|
119
|
+
end
|
120
|
+
else
|
121
|
+
log("\tvalue is not a collection")
|
122
|
+
unless value.respond_to?(:parent)
|
123
|
+
class << value
|
124
|
+
attr_accessor :parent
|
125
|
+
end
|
126
|
+
end
|
127
|
+
value.parent = node
|
128
|
+
model.send(:"#{ref.name}=",node_to_model(value))
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def log(msg)
|
134
|
+
puts msg if @@verbose
|
135
|
+
end
|
136
|
+
|
137
|
+
def node_to_model(node)
|
138
|
+
log("node_to_model #{node.class}")
|
139
|
+
metaclass = get_corresponding_metaclass(node)
|
140
|
+
instance = metaclass.new
|
141
|
+
metaclass.ecore.eAllAttributes.each do |attr|
|
142
|
+
populate_attr(node,attr,instance)
|
143
|
+
end
|
144
|
+
metaclass.ecore.eAllReferences.each do |ref|
|
145
|
+
populate_ref(node,ref,instance)
|
146
|
+
end
|
147
|
+
instance
|
148
|
+
end
|
149
|
+
|
150
|
+
def transform_enum_values(value)
|
151
|
+
if value.respond_to?(:java_class) && value.java_class.enum?
|
152
|
+
value.name
|
153
|
+
else
|
154
|
+
value
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def get_feature_value_through_getter(node,feat_name)
|
159
|
+
capitalized_name = feat_name.proper_capitalize
|
160
|
+
methods = [:"get#{capitalized_name}",:"is#{capitalized_name}"]
|
161
|
+
|
162
|
+
methods.each do |m|
|
163
|
+
if node.respond_to?(m)
|
164
|
+
begin
|
165
|
+
return transform_enum_values(node.send(m))
|
166
|
+
rescue Object => e
|
167
|
+
raise "Problem invoking #{m} on #{node.class}: #{e}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
raise "how should I get this... #{feat_name} on #{node.class}. It does not respond to #{methods}"
|
172
|
+
end
|
173
|
+
|
174
|
+
def get_feature_value(node,feat)
|
175
|
+
adapter = adapter(node.class,feat)
|
176
|
+
if adapter
|
177
|
+
#puts "Using adapter for #{node.class} #{feat_name}"
|
178
|
+
adapter[:adapter].call(node)
|
179
|
+
else
|
180
|
+
get_feature_value_through_getter(node,feat.name)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|