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 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