Merge pull request #41 from blackducksw/ubuntu_14
[ohcount] / ruby / gestalt / gestalt_engine.rb
1 module Ohcount
2   module Gestalt
3
4     # smells is a general class that aggregates facts required to deduce
5     # gestalts
6     class GestaltEngine
7       attr_reader :gestalts
8
9       def initialize
10         @language_facts = LanguageFacts.new
11         @gestalts = []
12         @source_file_rules = []
13         @definitions = Definition.all_definitions.map do |d|
14           new_definition = d.clone
15           new_definition.top_level_or.each_rule do |r|
16             @source_file_rules << r if r.is_a?(FileRule)
17           end
18           new_definition
19         end
20       end
21
22       def process_source_file(source_file)
23         @source_file_rules.each do |r|
24           r.process_source_file(source_file)
25         end
26         @language_facts.process_source_file(source_file)
27       end
28
29       def process_source_files(source_file_list)
30         source_file_list.each_source_file do |source_file|
31           process_source_file source_file
32         end
33         self
34       end
35
36       def process(what)
37         if what.is_a?(SourceFile)
38           process_source_file(what)
39         elsif what.is_a?(SourceFileList)
40           process_source_file_list(what)
41         else
42           raise ArgumentError.new
43         end
44       end
45
46       def calc_gestalts(definitions = nil)
47         # since gestalts can depend on other gestalts, we perform an
48         # iterative process and break when no new gestalts have been
49         # detected.
50         gestalt_count = 0
51
52         while true do
53           
54           # collect what we can
55           yank_definitions = []
56           @definitions.each do |d|
57
58             new_gestalts = d.gestalts(self)
59             if new_gestalts.any?
60               yank_definitions << d
61               @gestalts += new_gestalts
62             end
63           end
64           @definitions -= yank_definitions
65
66           # did we make any progress?
67           break if gestalt_count == @gestalts.size
68
69           # nope, keep going
70           gestalt_count = @gestalts.size
71         end
72
73         gestalts
74       end
75
76       def has_gestalt?(type,name)
77         @gestalts.find do |g|
78           g.type.to_s == type.to_s &&
79             g.name == name
80         end
81       end
82
83       def includes_language?(language, min_percent)
84         @language_facts.includes_language?(language,min_percent)
85       end
86     end
87
88
89     # keeps track of languages seen
90     class LanguageFacts
91
92       def initialize
93         @language_counts ||= {}
94       end
95
96       def process_source_file(source_file)
97         source_file.language_breakdowns.each do |lb|
98           @language_counts[lb.name.intern] ||= 0
99           @language_counts[lb.name.intern] += lb.code_count
100         end
101       end
102
103       def includes_language?(language, min_percent = 0)
104         return false unless @language_counts[language]
105         language_percents[language] >= min_percent
106       end
107
108       # ignores markup languages (xml/html)
109       def language_percents
110         @language_percents ||= begin
111           total = 0
112           @language_counts.each_pair do |name,code_count|
113                                                 language = Ohcount.ohcount_hash_language_from_name(name.to_s)
114                                                 STDOUT.puts "Warning: Couldn't find #{name} in ohcount_hash_language_from_name" if language.nil?
115                                                 next if language.nil? || language.category == 1
116             total += code_count
117           end
118
119           l = {}
120           @language_counts.each do |k,v|
121            l[k] = 100.0 * v.to_i / total
122           end
123           l
124
125         end
126       end
127     end
128
129   end
130 end
131