Browse Source

api improvements.

avkonst 12 năm trước cách đây
mục cha
commit
67f2ed4a6b

+ 138 - 46
mainline/core/api.py

@@ -17,8 +17,79 @@
 #    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-class Plugin(object):
+class Marker(object):
+    class T(object):
+        NONE            = 0x00
+        COMMENT         = 0x01
+        STRING          = 0x02
+        PREPROCESSOR    = 0x04
+        CODE            = 0x08
+        ALL_EXCEPT_CODE = 0x07
+        ANY             = 0xFF
+
+        def to_str(self, group):
+            if group == self.NONE:
+                return "none"
+            elif group == self.COMMENT:
+                return "comment"
+            elif group == self.STRING:
+                return "string"
+            elif group == self.PREPROCESSOR:
+                return "preprocessor"
+            elif group == self.CODE:
+                return "code"
+            else:
+                assert(False)
+        
+    def __init__(self, offset_begin, offset_end, group):
+        self.begin = offset_begin
+        self.end = offset_end
+        self.group = group
+        
+    def get_offset_begin(self):
+        return self.begin
+
+    def get_offset_end(self):
+        return self.end
+
+    def get_type(self):
+        return self.group
+
+
+class BasePlugin(object):
+    
+    def initialize(self):
+        pass
+
+    def terminate(self):
+        pass
     
+    def set_name(self, name):
+        self.name = name
+
+    def get_name(self):
+        if hasattr(self, 'name') == False:
+            return None
+        return self.name
+
+    def set_version(self, version):
+        self.version = version
+
+    def get_version(self):
+        if hasattr(self, 'version') == False:
+            return None
+        return self.version
+
+    def set_plugin_loader(self, loader):
+        self.plugin_loader = loader
+
+    def get_plugin_loader(self):
+        if hasattr(self, 'plugin_loader') == False:
+            return None
+        return self.plugin_loader
+
+class Plugin(BasePlugin):
+
     class Field(object):
         def __init__(self, name, ftype, non_zero=False):
             self.name = name
@@ -31,8 +102,13 @@ class Plugin(object):
             self.value = value
     
     def initialize(self, namespace=None, support_regions=True, fields=[], properties=[]):
-        self.is_updated = False
+        super(Plugin, self).initialize()
+        
+        if hasattr(self, 'is_updated') == False:
+            self.is_updated = False # original initialization
+            
         db_loader = self.get_plugin_loader().get_database_loader()
+        
         if namespace == None:
             namespace = self.get_name()
 
@@ -57,36 +133,54 @@ class Plugin(object):
                 # if field is created (not cloned from the previous db),
                 # mark the plug-in as updated in order to trigger full rescan
                 self.is_updated = self.is_updated or is_created
+
+class MetricPlugin(Plugin):
     
-    def terminate(self):
-        pass
+    def add_field(self, is_active, field,
+                       pattern_to_search,
+                       marker_type_mask=Marker.T.ANY,
+                       exclude_subregions=True,
+                       merge_markers=False):
+        if hasattr(self, '_fields') == False:
+            self._fields = {}
+        if is_active == True:
+            self._fields[field.name] = (field,
+                                        marker_type_mask,
+                                        exclude_subregions,
+                                        merge_markers,
+                                        pattern_to_search)
+            
+    def is_active(self, field_name = None):
+        if field_name == None:
+            return (len(self._fields.keys()) > 0)
+        return (field_name in self._fields.keys())
     
-    def set_name(self, name):
-        self.name = name
-
-    def get_name(self):
-        if hasattr(self, 'name') == False:
-            return None
-        return self.name
-
-    def set_version(self, version):
-        self.version = version
-
-    def get_version(self):
-        if hasattr(self, 'version') == False:
-            return None
-        return self.version
-
-    def set_plugin_loader(self, loader):
-        self.plugin_loader = loader
-
-    def get_plugin_loader(self):
-        if hasattr(self, 'plugin_loader') == False:
-            return None
-        return self.plugin_loader
+    def get_fields(self):
+        result = []
+        for key in self._fields.keys():
+            result.append(self._fields[key][0])
+        return result
+    
+    def count_if_active(self, field_name, data, namespace=None):
+        if self.is_active(field_name) == False:
+            return
+        
+        if namespace == None:
+            namespace = self.get_name()
+            
+        field_data = self._fields[field_name]
+        text = data.get_content()
+        for region in data.iterate_regions():
+            count = 0
+            for marker in data.iterate_markers(
+                            filter_group = field_data[1],
+                            region_id = region.get_id(),
+                            exclude_children = field_data[2],
+                            merge=field_data[3]):
+                count += len(field_data[4].findall(text, marker.get_offset_begin(), marker.get_offset_end()))
+            region.set_data(namespace, field_name, count)
 
 class InterfaceNotImplemented(Exception):
-    
     def __init__(self, obj):
         import sys
         Exception.__init__(self, "Method '"
@@ -95,10 +189,8 @@ class InterfaceNotImplemented(Exception):
                             + str(obj.__class__))
 
 class IConfigurable(object):
-    
     def configure(self, options):
         raise InterfaceNotImplemented(self)
-
     def declare_configuration(self, optparser):
         raise InterfaceNotImplemented(self)
 
@@ -110,6 +202,13 @@ class IParser(object):
     def process(self, parent, data, is_updated):
         raise InterfaceNotImplemented(self)
 
+class ICode(object):
+    pass
+
+class ITool(object):
+    def run(self, tool_args):
+        raise InterfaceNotImplemented(self)
+
 class CallbackNotImplemented(Exception):
     
     def __init__(self, obj, callback_name):
@@ -125,6 +224,15 @@ class Child(object):
             raise CallbackNotImplemented(self, callback_name)
         self.__getattribute__(callback_name)(parent, *args)
 
+    def subscribe_by_parents_name(self, parent_name, callback_name='callback'):
+        self.get_plugin_loader().get_plugin(parent_name).subscribe(self, callback_name)
+    
+    def subscribe_by_parents_interface(self, interface, callback_name='callback'):
+        for plugin in self.get_plugin_loader().iterate_plugins():
+            if isinstance(plugin, interface):
+                plugin.subscribe(self, callback_name)
+
+
 class Parent(object):
     
     def init_Parent(self):
@@ -164,22 +272,6 @@ class ExitError(Exception):
                                + reason)
             
 
-def subscribe_by_parents_name(parent_name, child, callback_name='callback'):
-    child.get_plugin_loader().get_plugin(parent_name).subscribe(child, callback_name)
-
-
-# interfaces for subscription
-class ICode(object):
-    pass
 
-def subscribe_by_parents_interface(interface, child, callback_name='callback'):
-    for plugin in child.get_plugin_loader().iterate_plugins():
-        if isinstance(plugin, interface):
-            plugin.subscribe(child, callback_name)
 
 
-class ITool(object):
-    
-    def run(self, tool_args):
-        raise InterfaceNotImplemented(self)
-

+ 1 - 37
mainline/core/db/loader.py

@@ -184,43 +184,7 @@ class FileRegionData(LoadableData):
     def iterate_subregion_ids(self):
         return self.children
 
-class Marker(object):
-    class T(object):
-        NONE            = 0x00
-        COMMENT         = 0x01
-        STRING          = 0x02
-        PREPROCESSOR    = 0x04
-        CODE            = 0x08
-        ALL_EXCEPT_CODE = 0x07
-        ANY             = 0xFF
-
-        def to_str(self, group):
-            if group == self.NONE:
-                return "none"
-            elif group == self.COMMENT:
-                return "comment"
-            elif group == self.STRING:
-                return "string"
-            elif group == self.PREPROCESSOR:
-                return "preprocessor"
-            elif group == self.CODE:
-                return "code"
-            else:
-                assert(False)
-        
-    def __init__(self, offset_begin, offset_end, group):
-        self.begin = offset_begin
-        self.end = offset_end
-        self.group = group
-        
-    def get_offset_begin(self):
-        return self.begin
-
-    def get_offset_end(self):
-        return self.end
-
-    def get_type(self):
-        return self.group
+from  core.api import Marker
 
 class FileData(LoadableData):
     

+ 3 - 3
mainline/ext/std/code/complexity.py

@@ -37,9 +37,9 @@ class Plugin(core.api.Plugin, core.api.Child, core.api.IConfigurable):
         core.api.Plugin.initialize(self, fields=fields)
         
         if len(fields) != 0:
-            core.api.subscribe_by_parents_name('std.code.cpp', self, 'callback_cpp')
-            core.api.subscribe_by_parents_name('std.code.cs', self, 'callback_cs')
-            core.api.subscribe_by_parents_name('std.code.java', self, 'callback_java')
+            self.subscribe_by_parents_name('std.code.cpp', 'callback_cpp')
+            self.subscribe_by_parents_name('std.code.cs', 'callback_cs')
+            self.subscribe_by_parents_name('std.code.java', 'callback_java')
 
     # cyclomatic complexity pattern
     # - C/C++

+ 1 - 1
mainline/ext/std/code/length.py

@@ -35,7 +35,7 @@ class Plugin(core.api.Plugin, core.api.Child, core.api.IConfigurable):
         core.api.Plugin.initialize(self, fields=fields)
         
         if len(fields) != 0:
-            core.api.subscribe_by_parents_interface(core.api.ICode, self, 'callback')
+            self.subscribe_by_parents_interface(core.api.ICode, 'callback')
 
     def callback(self, parent, data, is_updated):
         is_updated = is_updated or self.is_updated

+ 28 - 45
mainline/ext/std/code/lines.py

@@ -20,7 +20,7 @@
 import core.api
 import re
 
-class Plugin(core.api.Plugin, core.api.Child, core.api.IConfigurable):
+class Plugin(core.api.MetricPlugin, core.api.Child, core.api.IConfigurable):
     
     def declare_configuration(self, parser):
         parser.add_option("--std.code.lines.code", "--sclc", action="store_true", default=False,
@@ -46,53 +46,36 @@ class Plugin(core.api.Plugin, core.api.Child, core.api.IConfigurable):
         self.is_active_comments = options.__dict__['std.code.lines.comments']
         self.is_active_total = options.__dict__['std.code.lines.total']
         
+    pattern_line = re.compile(r'''[^\s].*''')
+
     def initialize(self):
-        fields = []
-        if self.is_active_code == True:
-            fields.append(self.Field('code', int))
-        if self.is_active_preprocessor == True:
-            fields.append(self.Field('preprocessor', int))
-        if self.is_active_comments == True:
-            fields.append(self.Field('comments', int))
-        if self.is_active_total == True:
-            fields.append(self.Field('total', int))
-        core.api.Plugin.initialize(self, fields=fields)
-        
-        if len(fields) != 0:
-            core.api.subscribe_by_parents_interface(core.api.ICode, self, 'callback')
+        self.add_field(self.is_active_code,
+                       self.Field('code', int),
+                       self.pattern_line,
+                       core.api.Marker.T.CODE)
+        self.add_field(self.is_active_preprocessor,
+                       self.Field('preprocessor', int),
+                       self.pattern_line,
+                       core.api.Marker.T.PREPROCESSOR)
+        self.add_field(self.is_active_comments,
+                       self.Field('comments', int),
+                       self.pattern_line,
+                       core.api.Marker.T.COMMENT)
+        self.add_field(self.is_active_total,
+                       self.Field('total', int),
+                       self.pattern_line,
+                       core.api.Marker.T.ANY,
+                       merge_markers=True)
 
-    pattern_line = re.compile(r'''[^\s].*''')
+        super(Plugin, self).initialize(fields=self.get_fields())
+
+        if self.is_active() == True:
+            self.subscribe_by_parents_interface(core.api.ICode, 'callback')
 
     def callback(self, parent, data, is_updated):
         is_updated = is_updated or self.is_updated
         if is_updated == True:
-            if self.is_active_code == True:
-                self.count_in_markers(data,
-                                   data.get_marker_types().CODE,
-                                   'code')
-            if self.is_active_preprocessor == True:
-                self.count_in_markers(data,
-                                      data.get_marker_types().PREPROCESSOR,
-                                      'preprocessor')
-            if self.is_active_comments == True:
-                self.count_in_markers(data,
-                                      data.get_marker_types().COMMENT,
-                                      'comments')
-            if self.is_active_total == True:
-                self.count_in_markers(data,
-                                   data.get_marker_types().ANY,
-                                   'total',
-                                   merge=True)
-    
-    def count_in_markers(self, data, marker_type, field_name, merge=False):
-        text = data.get_content()
-        for region in data.iterate_regions():
-            count = 0
-            for marker in data.iterate_markers(
-                            filter_group = marker_type,
-                            region_id = region.get_id(),
-                            exclude_children = True,
-                            merge=merge):
-                count += len(self.pattern_line.findall(text, marker.get_offset_begin(), marker.get_offset_end()))
-            region.set_data(self.get_name(), field_name, count)
-            
+            self.count_if_active('code', data)
+            self.count_if_active('preprocessor', data)
+            self.count_if_active('comments', data)
+            self.count_if_active('total', data)

+ 1 - 1
mainline/ext/std/code/test.py

@@ -26,7 +26,7 @@ class Plugin(core.api.Plugin, core.api.Child):
     def initialize(self):
         return
         # do not trigger version property set, it is a module for testing purposes
-        core.api.subscribe_by_parents_interface(core.api.ICode, self)
+        self.subscribe_by_parents_interface(core.api.ICode)
 
     def callback(self, parent, data, is_updated):
 

+ 1 - 1
mainline/ext/std/suppress.py

@@ -50,7 +50,7 @@ class Plugin(core.api.Plugin, core.api.Child, core.api.IConfigurable):
                                    fields=fields)
         
         if len(fields) != 0:
-            core.api.subscribe_by_parents_interface(core.api.ICode, self, 'callback')
+            self.subscribe_by_parents_interface(core.api.ICode)
 
     # suppress pattern
     pattern = re.compile(r'''metrix[+][+][:][ \t]+suppress[ \t]+([^ \t\r\n\*]+)''')

+ 33 - 33
mainline/tests/general/test_basic/test_std_lines_metrics_view_nest_per_file_stdout.gold.txt

@@ -21,8 +21,8 @@ data:
 .   .   .   .   .   offset_begin="0"
 .   .   .   .   data:  
 .   .   .   .   .   std.code.lines: 
-.   .   .   .   .   .   code="0"
 .   .   .   .   .   .   total="0"
+.   .   .   .   .   .   code="0"
 .   .   .   .   .   .   preprocessor="0"
 .   .   .   .   .   .   comments="0"
 .   .   .   .   subregions:
@@ -38,8 +38,8 @@ data:
 .   .   .   .   .   .   .   offset_begin="2"
 .   .   .   .   .   .   data:  
 .   .   .   .   .   .   .   std.code.lines: 
-.   .   .   .   .   .   .   .   code="4"
 .   .   .   .   .   .   .   .   total="5"
+.   .   .   .   .   .   .   .   code="4"
 .   .   .   .   .   .   .   .   preprocessor="0"
 .   .   .   .   .   .   .   .   comments="1"
 .   .   .   .   .   .   subregions:
@@ -55,8 +55,8 @@ data:
 .   .   .   .   .   .   .   .   .   offset_begin="76"
 .   .   .   .   .   .   .   .   data:  
 .   .   .   .   .   .   .   .   .   std.code.lines: 
-.   .   .   .   .   .   .   .   .   .   code="4"
 .   .   .   .   .   .   .   .   .   .   total="4"
+.   .   .   .   .   .   .   .   .   .   code="4"
 .   .   .   .   .   .   .   .   .   .   preprocessor="0"
 .   .   .   .   .   .   .   .   .   .   comments="0"
 .   .   .   .   .   .   .   .   subregions:
@@ -72,8 +72,8 @@ data:
 .   .   .   .   .   .   .   .   .   .   .   offset_begin="88"
 .   .   .   .   .   .   .   .   .   .   data:  
 .   .   .   .   .   .   .   .   .   .   .   std.code.lines: 
-.   .   .   .   .   .   .   .   .   .   .   .   code="11"
 .   .   .   .   .   .   .   .   .   .   .   .   total="12"
+.   .   .   .   .   .   .   .   .   .   .   .   code="11"
 .   .   .   .   .   .   .   .   .   .   .   .   preprocessor="0"
 .   .   .   .   .   .   .   .   .   .   .   .   comments="2"
 .   .   .   .   .   .   .   .   .   .   subregions:
@@ -89,8 +89,8 @@ data:
 .   .   .   .   .   .   .   .   .   .   .   offset_begin="237"
 .   .   .   .   .   .   .   .   .   .   data:  
 .   .   .   .   .   .   .   .   .   .   .   std.code.lines: 
-.   .   .   .   .   .   .   .   .   .   .   .   code="5"
 .   .   .   .   .   .   .   .   .   .   .   .   total="5"
+.   .   .   .   .   .   .   .   .   .   .   .   code="5"
 .   .   .   .   .   .   .   .   .   .   .   .   preprocessor="0"
 .   .   .   .   .   .   .   .   .   .   .   .   comments="0"
 .   .   .   .   .   .   .   .   .   .   subregions:
@@ -106,8 +106,8 @@ data:
 .   .   .   .   .   .   .   .   .   .   .   .   .   offset_begin="266"
 .   .   .   .   .   .   .   .   .   .   .   .   data:  
 .   .   .   .   .   .   .   .   .   .   .   .   .   std.code.lines: 
-.   .   .   .   .   .   .   .   .   .   .   .   .   .   code="3"
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   total="3"
+.   .   .   .   .   .   .   .   .   .   .   .   .   .   code="3"
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   preprocessor="0"
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   comments="0"
 .   .   .   .   .   .   .   .   .   .   .   .   subregions:
@@ -123,8 +123,8 @@ data:
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   offset_begin="287"
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   data:  
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   std.code.lines: 
-.   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   code="7"
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   total="8"
+.   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   code="7"
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   preprocessor="0"
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   .   comments="1"
 .   .   .   .   .   .   .   .   .   .   .   .   .   .   subregions:
@@ -140,8 +140,8 @@ data:
 .   .   .   .   .   .   .   .   .   .   .   offset_begin="389"
 .   .   .   .   .   .   .   .   .   .   data:  
 .   .   .   .   .   .   .   .   .   .   .   std.code.lines: 
-.   .   .   .   .   .   .   .   .   .   .   .   code="7"
 .   .   .   .   .   .   .   .   .   .   .   .   total="7"
+.   .   .   .   .   .   .   .   .   .   .   .   code="7"
 .   .   .   .   .   .   .   .   .   .   .   .   preprocessor="0"
 .   .   .   .   .   .   .   .   .   .   .   .   comments="0"
 .   .   .   .   .   .   .   .   .   .   subregions:
@@ -149,11 +149,11 @@ data:
 .   subdirs:
 .   aggregated-data:  
 .   .   std.code.lines:  
-.   .   .   code: 
+.   .   .   total: 
 .   .   .   .   count="8"
-.   .   .   .   max="11"
-.   .   .   .   avg="5.125"
-.   .   .   .   total="41.0"
+.   .   .   .   max="12"
+.   .   .   .   avg="5.5"
+.   .   .   .   total="44.0"
 .   .   .   .   min="0" 
 .   .   .   .   distribution-bars:
 .   .   .   .   
@@ -168,29 +168,34 @@ data:
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
-.   .   .   .   .   .   count="2"
+.   .   .   .   .   .   count="1"
 .   .   .   .   .   .   metric="4"
+.   .   .   .   .   .   ratio="0.125"
+.   .   .   .   
+.   .   .   .   .   distribution-bar: 
+.   .   .   .   .   .   count="2"
+.   .   .   .   .   .   metric="5"
 .   .   .   .   .   .   ratio="0.25"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="5"
+.   .   .   .   .   .   metric="7"
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
-.   .   .   .   .   .   count="2"
-.   .   .   .   .   .   metric="7"
-.   .   .   .   .   .   ratio="0.25"
+.   .   .   .   .   .   count="1"
+.   .   .   .   .   .   metric="8"
+.   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="11"
+.   .   .   .   .   .   metric="12"
 .   .   .   .   .   .   ratio="0.125"
-.   .   .   total: 
+.   .   .   code: 
 .   .   .   .   count="8"
-.   .   .   .   max="12"
-.   .   .   .   avg="5.5"
-.   .   .   .   total="44.0"
+.   .   .   .   max="11"
+.   .   .   .   avg="5.125"
+.   .   .   .   total="41.0"
 .   .   .   .   min="0" 
 .   .   .   .   distribution-bars:
 .   .   .   .   
@@ -205,28 +210,23 @@ data:
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
-.   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="4"
-.   .   .   .   .   .   ratio="0.125"
-.   .   .   .   
-.   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="2"
-.   .   .   .   .   .   metric="5"
+.   .   .   .   .   .   metric="4"
 .   .   .   .   .   .   ratio="0.25"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="7"
+.   .   .   .   .   .   metric="5"
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
-.   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="8"
-.   .   .   .   .   .   ratio="0.125"
+.   .   .   .   .   .   count="2"
+.   .   .   .   .   .   metric="7"
+.   .   .   .   .   .   ratio="0.25"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="12"
+.   .   .   .   .   .   metric="11"
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   preprocessor: 
 .   .   .   .   count="8"

+ 25 - 25
mainline/tests/general/test_basic/test_std_lines_metrics_view_txt_stdout.gold.txt

@@ -13,11 +13,11 @@ data:
 .   .   .
 .   aggregated-data:  
 .   .   std.code.lines:  
-.   .   .   code: 
+.   .   .   total: 
 .   .   .   .   count="8"
-.   .   .   .   max="11"
-.   .   .   .   avg="5.125"
-.   .   .   .   total="41.0"
+.   .   .   .   max="12"
+.   .   .   .   avg="5.5"
+.   .   .   .   total="44.0"
 .   .   .   .   min="0" 
 .   .   .   .   distribution-bars:
 .   .   .   .   
@@ -32,29 +32,34 @@ data:
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
-.   .   .   .   .   .   count="2"
+.   .   .   .   .   .   count="1"
 .   .   .   .   .   .   metric="4"
+.   .   .   .   .   .   ratio="0.125"
+.   .   .   .   
+.   .   .   .   .   distribution-bar: 
+.   .   .   .   .   .   count="2"
+.   .   .   .   .   .   metric="5"
 .   .   .   .   .   .   ratio="0.25"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="5"
+.   .   .   .   .   .   metric="7"
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
-.   .   .   .   .   .   count="2"
-.   .   .   .   .   .   metric="7"
-.   .   .   .   .   .   ratio="0.25"
+.   .   .   .   .   .   count="1"
+.   .   .   .   .   .   metric="8"
+.   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="11"
+.   .   .   .   .   .   metric="12"
 .   .   .   .   .   .   ratio="0.125"
-.   .   .   total: 
+.   .   .   code: 
 .   .   .   .   count="8"
-.   .   .   .   max="12"
-.   .   .   .   avg="5.5"
-.   .   .   .   total="44.0"
+.   .   .   .   max="11"
+.   .   .   .   avg="5.125"
+.   .   .   .   total="41.0"
 .   .   .   .   min="0" 
 .   .   .   .   distribution-bars:
 .   .   .   .   
@@ -69,28 +74,23 @@ data:
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
-.   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="4"
-.   .   .   .   .   .   ratio="0.125"
-.   .   .   .   
-.   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="2"
-.   .   .   .   .   .   metric="5"
+.   .   .   .   .   .   metric="4"
 .   .   .   .   .   .   ratio="0.25"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="7"
+.   .   .   .   .   .   metric="5"
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
-.   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="8"
-.   .   .   .   .   .   ratio="0.125"
+.   .   .   .   .   .   count="2"
+.   .   .   .   .   .   metric="7"
+.   .   .   .   .   .   ratio="0.25"
 .   .   .   .   
 .   .   .   .   .   distribution-bar: 
 .   .   .   .   .   .   count="1"
-.   .   .   .   .   .   metric="12"
+.   .   .   .   .   .   metric="11"
 .   .   .   .   .   .   ratio="0.125"
 .   .   .   preprocessor: 
 .   .   .   .   count="8"