codemodels 0.2.2-java

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ Gemfile.lock
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ coverage
7
+ InstalledFiles
8
+ lib/bundler/man
9
+ pkg
10
+ rdoc
11
+ spec/reports
12
+ test/tmp
13
+ test/version_tmp
14
+ tmp
15
+
16
+ # YARD artifacts
17
+ .yardoc
18
+ _yardoc
19
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
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
@@ -0,0 +1,9 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ end
7
+
8
+ desc "Run tests"
9
+ task :default => :test
@@ -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,11 @@
1
+ require 'rgen/metamodel_builder'
2
+
3
+ module CodeModels
4
+
5
+ def self.enable_foreign_asts(clazz)
6
+ clazz.class_eval do
7
+ contains_many_uni 'foreign_asts', CodeModelsAstNode
8
+ end
9
+ end
10
+
11
+ 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,11 @@
1
+ require 'rgen/metamodel_builder'
2
+ require 'codemodels/language'
3
+
4
+ module CodeModels
5
+
6
+ # All nodes should derive from this one
7
+ class CodeModelsAstNode < RGen::MetamodelBuilder::MMBase
8
+ include CodeModelsReflectionInfo
9
+ end
10
+
11
+ 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