codemodels 0.2.2-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +19 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +23 -0
- data/Rakefile +9 -0
- data/codemodels.gemspec +29 -0
- data/lib/codemodels/foreign_ast.rb +11 -0
- data/lib/codemodels/info_extraction.rb +139 -0
- data/lib/codemodels/language.rb +65 -0
- data/lib/codemodels/metamodel.rb +11 -0
- data/lib/codemodels/model_building.rb +81 -0
- data/lib/codemodels/monkey_patching.rb +30 -0
- data/lib/codemodels/parsing.rb +184 -0
- data/lib/codemodels/query_serialized.rb +81 -0
- data/lib/codemodels/rgen_ext.rb +186 -0
- data/lib/codemodels/serialization.rb +142 -0
- data/lib/codemodels/version.rb +3 -0
- data/lib/codemodels.rb +5 -0
- data/test/data/node_setCompleted.json +443 -0
- data/test/test_foreign_ast.rb +28 -0
- data/test/test_helper.rb +10 -0
- data/test/test_info_extraction.rb +90 -0
- data/test/test_language.rb +28 -0
- data/test/test_monkey_patching.rb +38 -0
- data/test/test_query_serialized.rb +40 -0
- data/test/test_rgen_ext.rb +187 -0
- data/test/test_serialization.rb +83 -0
- metadata +167 -0
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,23 @@
|
|
1
|
+
codemodels
|
2
|
+
===========
|
3
|
+
|
4
|
+
codemodels is a library to create and manipulate models. A serialization format (based on JSON) is defined.
|
5
|
+
|
6
|
+
It is based on [RGen](http://github.com/mthiede/rgen) and it supportes the conversion of EMF models through [emf_jruby](http://github.com/ftomassetti/emf_jruby).
|
7
|
+
|
8
|
+
There are different gems which transform source code in models of the code. Currently they are:
|
9
|
+
* [html-lightmodels](http://github.com/ftomassetti/html-lightmodels)
|
10
|
+
* [java-lightmodels](http://github.com/ftomassetti/java-lightmodels)
|
11
|
+
* [js-lightmodels](http://github.com/ftomassetti/js-lightmodels)
|
12
|
+
* [properties-lightmodels](http://github.com/ftomassetti/properties-lightmodels)
|
13
|
+
* [ruby-lightmodels](http://github.com/ftomassetti/ruby-lightmodels)
|
14
|
+
* [xml-lightmodels](http://github.com/ftomassetti/xml-lightmodels)
|
15
|
+
|
16
|
+
Therefore it can be used to perform analysis on different languages.
|
17
|
+
|
18
|
+
Features
|
19
|
+
========
|
20
|
+
|
21
|
+
It handles also the case in which different languages are hosted in the same file (for example a Javascript script inside an HTML page).
|
22
|
+
|
23
|
+
Most of the parsers permit also to associate each node with a precise point in the source code, so thath they could be used to implement layout-preserving refactoring.
|
data/Rakefile
ADDED
data/codemodels.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'codemodels/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.platform = 'java'
|
8
|
+
s.name = 'codemodels'
|
9
|
+
s.version = CodeModels::VERSION
|
10
|
+
s.date = '2013-08-27'
|
11
|
+
s.summary = "Library to build models of code"
|
12
|
+
s.description = "Library to build models of code of different languages in a uniform way."
|
13
|
+
s.authors = ["Federico Tomassetti"]
|
14
|
+
s.email = 'f.tomassetti@gmail.com'
|
15
|
+
s.homepage = 'https://github.com/ftomassetti/codemodels'
|
16
|
+
s.license = "APACHE 2"
|
17
|
+
|
18
|
+
s.files = `git ls-files`.split($/)
|
19
|
+
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
|
23
|
+
s.add_dependency('json')
|
24
|
+
s.add_dependency('rgen')
|
25
|
+
|
26
|
+
s.add_development_dependency "bundler", "1.3.5"
|
27
|
+
s.add_development_dependency "rake"
|
28
|
+
s.add_development_dependency "simplecov"
|
29
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'rgen/metamodel_builder'
|
2
|
+
|
3
|
+
module CodeModels
|
4
|
+
module InfoExtraction
|
5
|
+
|
6
|
+
class TermsBreaker
|
7
|
+
|
8
|
+
attr_accessor :sequences, :inv_sequences
|
9
|
+
|
10
|
+
def initialize(language_specific_logic)
|
11
|
+
@language_specific_logic = language_specific_logic
|
12
|
+
@sequences = Hash.new {|h,k|
|
13
|
+
h[k] = Hash.new {|h,k|
|
14
|
+
h[k]=0
|
15
|
+
}
|
16
|
+
}
|
17
|
+
@inv_sequences = Hash.new {|h,k|
|
18
|
+
h[k] = Hash.new {|h2,k2|
|
19
|
+
h2[k2]=0
|
20
|
+
}
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_context(language_specific_logic,context)
|
25
|
+
values_map = context.values_map
|
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
|
+
module InfoExtractionFunctionalities
|
104
|
+
|
105
|
+
def values_map
|
106
|
+
collect_values_with_count_subtree
|
107
|
+
end
|
108
|
+
|
109
|
+
def terms_map(language_specific_logic,context=nil)
|
110
|
+
# context default to root
|
111
|
+
unless context
|
112
|
+
context = self
|
113
|
+
while context.eContainer
|
114
|
+
context = context.eContainer
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# look into context to see how frequent are certain series of words,
|
119
|
+
# frequent series are recognized as composed terms
|
120
|
+
terms_breaker = TermsBreaker.from_context(language_specific_logic,context)
|
121
|
+
|
122
|
+
v_map = self.values_map
|
123
|
+
terms_map = Hash.new {|h,k| h[k]=0}
|
124
|
+
v_map.each do |v,n|
|
125
|
+
terms_breaker.terms_in_value(v).each do |t|
|
126
|
+
terms_map[t] += n
|
127
|
+
end
|
128
|
+
end
|
129
|
+
terms_map
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
class ::RGen::MetamodelBuilder::MMBase
|
135
|
+
include InfoExtractionFunctionalities
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module CodeModels
|
2
|
+
|
3
|
+
@@languages = []
|
4
|
+
|
5
|
+
class Parser
|
6
|
+
|
7
|
+
def parse_file(path)
|
8
|
+
code = IO.read(path)
|
9
|
+
parse_code(code)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
module CodeModelsReflectionInfo
|
15
|
+
attr_accessor :source
|
16
|
+
attr_accessor :language
|
17
|
+
end
|
18
|
+
|
19
|
+
class Position
|
20
|
+
attr_accessor :line, :column
|
21
|
+
end
|
22
|
+
|
23
|
+
class SourceInfo
|
24
|
+
attr_accessor :filename
|
25
|
+
attr_accessor :begin_pos, :end_pos
|
26
|
+
|
27
|
+
def to_code
|
28
|
+
raise "Unimplemented"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class Language
|
34
|
+
attr_reader :name
|
35
|
+
attr_reader :extensions
|
36
|
+
attr_reader :parser
|
37
|
+
|
38
|
+
def initialize(name)
|
39
|
+
@name = name
|
40
|
+
@extensions = []
|
41
|
+
end
|
42
|
+
|
43
|
+
def can_parse?(path)
|
44
|
+
extension = File.extname(path)
|
45
|
+
extension=extension[1..-1] if extension.length>0
|
46
|
+
@extensions.include?(extension)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.register_language(language)
|
52
|
+
@@languages << language
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.registered_languages
|
56
|
+
@@languages
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.parse_file(path)
|
60
|
+
l = @@languages.find {|l| l.can_parse?(path) }
|
61
|
+
raise "I don't know how to parse #{path}. Languages known: #{@@languages.map(&:name)}" unless l
|
62
|
+
l.parser.parse_file(path)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'codemodels/serialization'
|
2
|
+
|
3
|
+
module CodeModels
|
4
|
+
|
5
|
+
module ModelBuilding
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :verbose
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.verbose_warn(msg)
|
12
|
+
warn(msg) if verbose
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.handle_serialized_models_in_dir(src,src_extension,error_handler=nil,model_handler,&model_creator)
|
16
|
+
Dir["#{src}/**/*.#{src_extension}"].each do |fd|
|
17
|
+
verbose_warn "== #{fd} =="
|
18
|
+
handle_serialized_model_per_file(fd,error_handler,model_handler,&model_creator)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.handle_models_in_dir(src,src_extension,error_handler=nil,model_handler,&model_creator)
|
23
|
+
Dir["#{src}/**/*.#{src_extension}"].each do |fd|
|
24
|
+
verbose_warn "== #{fd} =="
|
25
|
+
handle_model_per_file(fd,error_handler,model_handler,&model_creator)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.generate_models_in_dir(src,dest,src_extension,dest_extension,max_nesting=500,error_handler=nil,&model_creator)
|
30
|
+
verbose_warn "== #{src} -> #{dest} =="
|
31
|
+
Dir["#{src}/**/*.#{src_extension}"].each do |fd|
|
32
|
+
if File.directory? fd
|
33
|
+
basename = File.basename(fd)
|
34
|
+
generate_models_in_dir("#{src}/#{basename}","#{dest}/#{basename}",src_extension,dest_extension,max_nesting,error_handler,&model_creator)
|
35
|
+
else
|
36
|
+
if File.extname(fd)==".#{src_extension}"
|
37
|
+
translated_simple_name = "#{File.basename(fd, ".#{src_extension}")}.#{dest_extension}"
|
38
|
+
translated_name = "#{dest}/#{translated_simple_name}"
|
39
|
+
verbose_warn "* #{fd} --> #{translated_name}"
|
40
|
+
generate_model_per_file(fd,translated_name,max_nesting,error_handler,&model_creator)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.handle_model_per_file(src,error_handler=nil,model_handler,&models_generator)
|
47
|
+
verbose_warn "<Model from #{src}>"
|
48
|
+
begin
|
49
|
+
m = models_generator.call(src)
|
50
|
+
model_handler.call(src,m)
|
51
|
+
rescue => e
|
52
|
+
if error_handler
|
53
|
+
error_handler.call(src,e)
|
54
|
+
else
|
55
|
+
raise e
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.generate_model_per_file(src,dest,max_nesting=500,error_handler=nil,&models_generator)
|
61
|
+
if not File.exist? dest
|
62
|
+
verbose_warn "<Model from #{src}>"
|
63
|
+
|
64
|
+
begin
|
65
|
+
m = models_generator.call(src)
|
66
|
+
LightModels::Serialization.save_model(m,dest,max_nesting)
|
67
|
+
rescue Exception => e
|
68
|
+
if error_handler
|
69
|
+
error_handler.call(src,e)
|
70
|
+
else
|
71
|
+
raise e
|
72
|
+
end
|
73
|
+
end
|
74
|
+
else
|
75
|
+
verbose_warn "skipping #{src} because #{dest} found"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -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,184 @@
|
|
1
|
+
require 'codemodels/monkey_patching'
|
2
|
+
|
3
|
+
module CodeModels
|
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
|
+
model.send(:"#{att.name}=",value) if value!=nil
|
92
|
+
rescue Object => e
|
93
|
+
puts "Problem while populating attribute #{att.name} of #{model} from #{node}. Value: #{value}"
|
94
|
+
raise e
|
95
|
+
end
|
96
|
+
|
97
|
+
def populate_ref(node,ref,model)
|
98
|
+
log("populate ref #{ref.name}, node: #{node.class}, model: #{model.class}")
|
99
|
+
value = get_feature_value(node,ref)
|
100
|
+
log("\tvalue=#{value.class}")
|
101
|
+
if value!=nil
|
102
|
+
if value==node
|
103
|
+
puts "avoiding loop... #{ref.name}, class #{node.class}"
|
104
|
+
return
|
105
|
+
end
|
106
|
+
if JavaCollection.assignable_from?(value.java_class)
|
107
|
+
log("\tvalue is a collection")
|
108
|
+
capitalized_name = ref.name.proper_capitalize
|
109
|
+
value.each do |el|
|
110
|
+
unless el.respond_to?(:parent)
|
111
|
+
class << el
|
112
|
+
attr_accessor :parent
|
113
|
+
end
|
114
|
+
end
|
115
|
+
el.parent = node
|
116
|
+
model.send(:"add#{capitalized_name}",node_to_model(el))
|
117
|
+
end
|
118
|
+
else
|
119
|
+
log("\tvalue is not a collection")
|
120
|
+
unless value.respond_to?(:parent)
|
121
|
+
value.class.__persistent__ = true
|
122
|
+
class << value
|
123
|
+
attr_accessor :parent
|
124
|
+
end
|
125
|
+
end
|
126
|
+
value.parent = node
|
127
|
+
model.send(:"#{ref.name}=",node_to_model(value))
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
def log(msg)
|
133
|
+
puts msg if @@verbose
|
134
|
+
end
|
135
|
+
|
136
|
+
def node_to_model(node)
|
137
|
+
log("node_to_model #{node.class}")
|
138
|
+
metaclass = get_corresponding_metaclass(node)
|
139
|
+
instance = metaclass.new
|
140
|
+
metaclass.ecore.eAllAttributes.each do |attr|
|
141
|
+
populate_attr(node,attr,instance)
|
142
|
+
end
|
143
|
+
metaclass.ecore.eAllReferences.each do |ref|
|
144
|
+
populate_ref(node,ref,instance)
|
145
|
+
end
|
146
|
+
instance
|
147
|
+
end
|
148
|
+
|
149
|
+
def transform_enum_values(value)
|
150
|
+
if value.respond_to?(:java_class) && value.java_class.enum?
|
151
|
+
value.name
|
152
|
+
else
|
153
|
+
value
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def get_feature_value_through_getter(node,feat_name)
|
158
|
+
capitalized_name = feat_name.proper_capitalize
|
159
|
+
methods = [:"get#{capitalized_name}",:"is#{capitalized_name}"]
|
160
|
+
|
161
|
+
methods.each do |m|
|
162
|
+
if node.respond_to?(m)
|
163
|
+
begin
|
164
|
+
return transform_enum_values(node.send(m))
|
165
|
+
rescue Object => e
|
166
|
+
raise "Problem invoking #{m} on #{node.class}: #{e}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
raise "how should I get this... #{feat_name} on #{node.class}. It does not respond to #{methods}"
|
171
|
+
end
|
172
|
+
|
173
|
+
def get_feature_value(node,feat)
|
174
|
+
adapter = adapter(node.class,feat)
|
175
|
+
if adapter
|
176
|
+
adapter[:adapter].call(node)
|
177
|
+
else
|
178
|
+
get_feature_value_through_getter(node,feat.name)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|