avkonst 12 년 전
부모
커밋
d8d501deb3
100개의 변경된 파일7163개의 추가작업 그리고 0개의 파일을 삭제
  1. 17 0
      releases/metrixplusplus-1.1.70/.project
  2. 11 0
      releases/metrixplusplus-1.1.70/.pydevproject
  3. 31 0
      releases/metrixplusplus-1.1.70/changelog.txt
  4. 18 0
      releases/metrixplusplus-1.1.70/core/__init__.py
  5. 153 0
      releases/metrixplusplus-1.1.70/core/api.py
  6. 39 0
      releases/metrixplusplus-1.1.70/core/cmdparser.py
  7. 18 0
      releases/metrixplusplus-1.1.70/core/db/__init__.py
  8. 910 0
      releases/metrixplusplus-1.1.70/core/db/loader.py
  9. 52 0
      releases/metrixplusplus-1.1.70/core/db/post.py
  10. 639 0
      releases/metrixplusplus-1.1.70/core/db/sqlite.py
  11. 93 0
      releases/metrixplusplus-1.1.70/core/db/utils.py
  12. 119 0
      releases/metrixplusplus-1.1.70/core/dir.py
  13. 18 0
      releases/metrixplusplus-1.1.70/core/export/__init__.py
  14. 38 0
      releases/metrixplusplus-1.1.70/core/export/convert.py
  15. 40 0
      releases/metrixplusplus-1.1.70/core/export/cout.py
  16. 18 0
      releases/metrixplusplus-1.1.70/core/export/utils/__init__.py
  17. 138 0
      releases/metrixplusplus-1.1.70/core/export/utils/py2txt.py
  18. 141 0
      releases/metrixplusplus-1.1.70/core/export/utils/py2xml.py
  19. 26 0
      releases/metrixplusplus-1.1.70/core/ext-priority/core.db.post.ini
  20. 26 0
      releases/metrixplusplus-1.1.70/core/ext-priority/core.dir.ini
  21. 26 0
      releases/metrixplusplus-1.1.70/core/ext-priority/core.log.ini
  22. 133 0
      releases/metrixplusplus-1.1.70/core/loader.py
  23. 56 0
      releases/metrixplusplus-1.1.70/core/log.py
  24. 130 0
      releases/metrixplusplus-1.1.70/core/warn.py
  25. 35 0
      releases/metrixplusplus-1.1.70/doc/index-online.html
  26. 35 0
      releases/metrixplusplus-1.1.70/doc/index.html
  27. 674 0
      releases/metrixplusplus-1.1.70/doc/license.txt
  28. 69 0
      releases/metrixplusplus-1.1.70/doc/limitations.txt
  29. BIN
      releases/metrixplusplus-1.1.70/doc/logo.png
  30. BIN
      releases/metrixplusplus-1.1.70/doc/logo_project.png
  31. BIN
      releases/metrixplusplus-1.1.70/doc/logo_small.gif
  32. BIN
      releases/metrixplusplus-1.1.70/doc/logo_small.png
  33. 546 0
      releases/metrixplusplus-1.1.70/doc/project.html
  34. 130 0
      releases/metrixplusplus-1.1.70/doc/style.css
  35. 0 0
      releases/metrixplusplus-1.1.70/ext/std/__init__.py
  36. 18 0
      releases/metrixplusplus-1.1.70/ext/std/code/__init__.py
  37. 26 0
      releases/metrixplusplus-1.1.70/ext/std/code/complexity.ini
  38. 75 0
      releases/metrixplusplus-1.1.70/ext/std/code/complexity.py
  39. 26 0
      releases/metrixplusplus-1.1.70/ext/std/code/cpp.ini
  40. 304 0
      releases/metrixplusplus-1.1.70/ext/std/code/cpp.py
  41. 26 0
      releases/metrixplusplus-1.1.70/ext/std/code/cs.ini
  42. 319 0
      releases/metrixplusplus-1.1.70/ext/std/code/cs.py
  43. 26 0
      releases/metrixplusplus-1.1.70/ext/std/code/java.ini
  44. 278 0
      releases/metrixplusplus-1.1.70/ext/std/code/java.py
  45. 26 0
      releases/metrixplusplus-1.1.70/ext/std/code/test.ini
  46. 37 0
      releases/metrixplusplus-1.1.70/ext/std/code/test.py
  47. 75 0
      releases/metrixplusplus-1.1.70/metrixpp.py
  48. 50 0
      releases/metrixplusplus-1.1.70/readme.txt
  49. 18 0
      releases/metrixplusplus-1.1.70/tests/__init__.py
  50. 255 0
      releases/metrixplusplus-1.1.70/tests/common.py
  51. 186 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic.py
  52. 0 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/sources/.unused.cpp
  53. 0 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/sources/dummy.txt
  54. 51 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/sources/simple.cpp
  55. 0 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/sources_changed/.unused.cpp
  56. 64 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/sources_changed/simple.cpp
  57. 25 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/sources_changed/simple2.cpp
  58. 0 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_collect_default_stdout.gold.txt
  59. 0 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_collect_nest_stdout.gold.txt
  60. 101 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_nest_per_file_stdout.gold.txt
  61. 19 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_nest_stdout.gold.txt
  62. 1 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_python_stdout.gold.txt
  63. 23 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_txt_stdout.gold.txt
  64. 17 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_xml_stdout.gold.txt
  65. 43 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_help_collect_default_stdout.gold.txt
  66. 32 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_help_export_default_stdout.gold.txt
  67. 19 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_help_info_default_stdout.gold.txt
  68. 41 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_help_limit_default_stdout.gold.txt
  69. 5 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_collect_default_stderr.gold.txt
  70. 0 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_collect_default_stdout.gold.txt
  71. 5 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_collect_second_stderr.gold.txt
  72. 0 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_collect_second_stdout.gold.txt
  73. 3 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_default_stderr.gold.txt
  74. 17 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_default_stdout.gold.txt
  75. 3 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_second_per_file_stderr.gold.txt
  76. 81 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_second_per_file_stdout.gold.txt
  77. 3 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_second_stderr.gold.txt
  78. 19 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_second_stdout.gold.txt
  79. 2 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_info_default_stderr.gold.txt
  80. 16 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_info_default_stdout.gold.txt
  81. 2 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_info_second_stderr.gold.txt
  82. 17 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_info_second_stdout.gold.txt
  83. 4 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_default_stderr.gold.txt
  84. 32 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_default_stdout.gold.txt
  85. 4 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_stderr.gold.txt
  86. 48 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_stdout.gold.txt
  87. 4 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_all_stderr.gold.txt
  88. 48 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_all_stdout.gold.txt
  89. 5 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_new_stderr.gold.txt
  90. 16 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_new_stdout.gold.txt
  91. 5 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_touched_stderr.gold.txt
  92. 32 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_touched_stdout.gold.txt
  93. 5 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_trend_stderr.gold.txt
  94. 24 0
      releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_trend_stdout.gold.txt
  95. 46 0
      releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp.py
  96. 19 0
      releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/function_ends_on_class.cpp
  97. 85 0
      releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/operator_test.hpp
  98. 1 0
      releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/test.c
  99. 52 0
      releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/test2.cpp
  100. 0 0
      releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/test3.cpp

+ 17 - 0
releases/metrixplusplus-1.1.70/.project

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>Metrix++</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.python.pydev.PyDevBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.python.pydev.pythonNature</nature>
+	</natures>
+</projectDescription>

+ 11 - 0
releases/metrixplusplus-1.1.70/.pydevproject

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?eclipse-pydev version="1.0"?>
+
+<pydev_project>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
+<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.7</pydev_property>
+<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
+<path>/Metrix++</path>
+<path>/Metrix++/ext</path>
+</pydev_pathproperty>
+</pydev_project>

+ 31 - 0
releases/metrixplusplus-1.1.70/changelog.txt

@@ -0,0 +1,31 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+===============================================================================
+
+    Metrix++ 1.1.*
+     - Extension point for post analysis tools. All tools are merged
+       to one 'metrixpp.py' with plugable actions, like collect, limit, etc:
+       run 'python metrixpp.py' to get the list of actions supported.
+     - fixed Java parser (fixed false match of attributes as functions)
+
+    Metrix++ 1.0.*
+     - Changelog file started
+
+===============================================================================

+ 18 - 0
releases/metrixplusplus-1.1.70/core/__init__.py

@@ -0,0 +1,18 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#

+ 153 - 0
releases/metrixplusplus-1.1.70/core/api.py

@@ -0,0 +1,153 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+class Plugin(object):
+    
+    def initialize(self):
+        self.is_updated = False
+        db_loader = self.get_plugin_loader().get_database_loader()
+        prev_version = db_loader.set_property(self.get_name() + ":version", self.get_version())
+        if prev_version != self.get_version():
+            self.is_updated = True
+    
+    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 InterfaceNotImplemented(Exception):
+    
+    def __init__(self, obj):
+        import sys
+        Exception.__init__(self, "Method '"
+                            + sys._getframe(1).f_code.co_name
+                            + "' has not been implemented for "
+                            + str(obj.__class__))
+
+class IConfigurable(object):
+    
+    def configure(self, options):
+        raise InterfaceNotImplemented(self)
+
+    def declare_configuration(self, optparser):
+        raise InterfaceNotImplemented(self)
+
+class IRunable(object):
+    def run(self, args):
+        raise InterfaceNotImplemented(self)
+    
+class IParser(object):
+    def process(self, parent, data, is_updated):
+        raise InterfaceNotImplemented(self)
+
+class CallbackNotImplemented(Exception):
+    
+    def __init__(self, obj, callback_name):
+        Exception.__init__(self, "Callback '"
+                           + callback_name
+                           + "' has not been implemented for "
+                           + str(obj.__class__))
+
+class Child(object):
+    
+    def notify(self, parent, callback_name, *args):
+        if hasattr(self, callback_name) == False:
+            raise CallbackNotImplemented(self, callback_name)
+        self.__getattribute__(callback_name)(parent, *args)
+
+class Parent(object):
+    
+    def init_Parent(self):
+        if hasattr(self, 'children') == False:
+            self.children = []
+            
+    def subscribe(self, obj, callback_name):
+        self.init_Parent()
+        if (isinstance(obj, Child) == False):
+            raise TypeError()
+        self.children.append((obj,callback_name))
+
+    def unsubscribe(self, obj, callback_name):
+        self.init_Parent()
+        self.children.remove((obj, callback_name))
+
+    def notify_children(self, *args):
+        self.init_Parent()
+        for child in self.children:
+            child[0].notify(self, child[1], *args)
+
+    def iterate_children(self):
+        self.init_Parent()
+        for child in self.children:
+            yield child
+
+class ExitError(Exception):
+    def __init__(self, plugin, reason):
+        if plugin != None:
+            Exception.__init__(self, "Plugin '"
+                               + plugin.get_name()
+                               + "' requested abnormal termination: "
+                               + reason)
+        else:
+            Exception.__init__(self, "'Abnormal termination requested: "
+                               + 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)
+
+

+ 39 - 0
releases/metrixplusplus-1.1.70/core/cmdparser.py

@@ -0,0 +1,39 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import optparse
+
+class MultiOptionParser(optparse.OptionParser):
+    
+    class MultipleOption(optparse.Option):
+        ACTIONS = optparse.Option.ACTIONS + ("multiopt",)
+        STORE_ACTIONS = optparse.Option.STORE_ACTIONS + ("multiopt",)
+        TYPED_ACTIONS = optparse.Option.TYPED_ACTIONS + ("multiopt",)
+        ALWAYS_TYPED_ACTIONS = optparse.Option.ALWAYS_TYPED_ACTIONS + ("multiopt",)
+    
+        def take_action(self, action, dest, opt, value, values, parser):
+            if action == "multiopt":
+                values.ensure_value(dest, []).append(value)
+            else:
+                optparse.Option.take_action(self, action, dest, opt, value, values, parser)
+
+    
+    def __init__(self, *args, **kwargs):
+        optparse.OptionParser.__init__(self, *args, option_class=self.MultipleOption, **kwargs)
+        

+ 18 - 0
releases/metrixplusplus-1.1.70/core/db/__init__.py

@@ -0,0 +1,18 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#

+ 910 - 0
releases/metrixplusplus-1.1.70/core/db/loader.py

@@ -0,0 +1,910 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import logging
+import os.path
+
+import core.api
+import core.db.sqlite
+
+####################################
+# Data Interface
+####################################
+
+class Data(object):
+
+    def __init__(self):
+        self.data = {}
+
+    def get_data(self, namespace, field):
+        if namespace not in self.data.keys():
+            return None
+        if field not in self.data[namespace].keys():
+            return None
+        return self.data[namespace][field]
+
+    def set_data(self, namespace, field, value):
+        if namespace not in self.data:
+            self.data[namespace] = {}
+        self.data[namespace][field] = value
+    
+    def iterate_namespaces(self):
+        for namespace in self.data.keys():
+            yield namespace
+            
+    def iterate_fields(self, namespace):
+        for field in self.data[namespace].keys():
+            yield (field, self.data[namespace][field])
+
+    def get_data_tree(self, namespaces=None):
+        return self.data
+
+    def __repr__(self):
+        return object.__repr__(self) + " with data " + self.data.__repr__()
+
+
+class LoadableData(Data):
+    
+    def __init__(self, loader, file_id, region_id):
+        Data.__init__(self)
+        self.loader = loader
+        self.file_id = file_id
+        self.region_id = region_id
+        self.loaded_namespaces = []
+        self.changed_namespaces = []
+
+    def load_namespace(self, namespace):
+        try:
+            row = self.loader.db.get_row(namespace, self.file_id, self.region_id)
+        except Exception:
+            logging.debug("No data in the database for namespace: " + namespace)
+            return
+        if row == None:
+            return 
+        for column_name in row.keys():
+            packager = self.loader.get_namespace(namespace).get_field_packager(column_name)
+            if packager == None:
+                continue
+            Data.set_data(self, namespace, column_name, packager.unpack(row[column_name]))
+        
+    def set_data(self, namespace, field, value):
+        if namespace not in self.changed_namespaces:
+            self.changed_namespaces.append(namespace)
+        return Data.set_data(self, namespace, field, value)
+
+    def get_data(self, namespace, field):
+        if namespace not in self.loaded_namespaces:
+            self.loaded_namespaces.append(namespace)
+            self.load_namespace(namespace)
+        return Data.get_data(self, namespace, field)
+    
+    def is_namespace_updated(self, namespace):
+        return namespace in self.changed_namespaces
+
+    def is_namespace_loaded(self, namespace):
+        return namespace in self.loaded_namespaces
+
+    def get_data_tree(self, namespaces=None):
+        if namespaces == None:
+            namespaces = self.loader.iterate_namespace_names()
+        for each in namespaces:
+            self.load_namespace(each)
+        return Data.get_data_tree(self)
+    
+class FileRegionData(LoadableData):
+    class T(object):
+        NONE      = 0x00
+        GLOBAL    = 0x01
+        CLASS     = 0x02
+        STRUCT    = 0x04
+        NAMESPACE = 0x08
+        FUNCTION  = 0x10
+        INTERFACE = 0x20
+        ANY       = 0xFFFFFFFF
+        
+        def to_str(self, group):
+            if group == self.NONE:
+                return "none"
+            elif group == self.GLOBAL:
+                return "global"
+            elif group == self.CLASS:
+                return "class"
+            elif group == self.STRUCT:
+                return "struct"
+            elif group == self.NAMESPACE:
+                return "namespace"
+            elif group == self.FUNCTION:
+                return "function"
+            elif group == self.INTERFACE:
+                return "interface"
+            else:
+                assert(False)
+    
+    def __init__(self, loader, file_id, region_id, region_name, offset_begin, offset_end, line_begin, line_end, cursor_line, group, checksum):
+        LoadableData.__init__(self, loader, file_id, region_id)
+        self.name = region_name
+        self.begin = offset_begin
+        self.end = offset_end
+        self.line_begin = line_begin
+        self.line_end = line_end
+        self.cursor = cursor_line
+        self.group = group
+        self.checksum = checksum
+        
+        self.children = []
+    
+    def get_id(self):
+        return self.region_id
+
+    def get_name(self):
+        return self.name
+
+    def get_offset_begin(self):
+        return self.begin
+
+    def get_offset_end(self):
+        return self.end
+
+    def get_line_begin(self):
+        return self.line_begin
+
+    def get_line_end(self):
+        return self.line_end
+
+    def get_cursor(self):
+        return self.cursor
+
+    def get_type(self):
+        return self.group
+
+    def get_checksum(self):
+        return self.checksum
+    
+    def register_subregion_id(self, child_id):
+        self.children.append(child_id)
+
+    def iterate_subregion_ids(self):
+        return self.children
+
+class Marker(object):
+    class T(object):
+        NONE            = 0x00
+        COMMENT         = 0x01
+        STRING          = 0x02
+        PREPROCESSOR    = 0x04
+        ALL_EXCEPT_CODE = 0x07
+
+        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"
+            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 FileData(LoadableData):
+    
+    def __init__(self, loader, path, file_id, checksum, content):
+        LoadableData.__init__(self, loader, file_id, None)
+        self.path = path
+        self.checksum = checksum
+        self.content = content
+        self.regions = None
+        self.markers = None
+        self.loader = loader
+        self.loading_tmp = []
+        
+    def get_id(self):
+        return self.file_id
+
+    def get_path(self):
+        return self.path
+
+    def get_checksum(self):
+        return self.checksum
+    
+    def get_content(self, exclude = Marker.T.NONE):
+        if exclude == Marker.T.NONE:
+            return self.content
+        
+        if exclude == (Marker.T.COMMENT | Marker.T.STRING | Marker.T.PREPROCESSOR):
+            # optimise frequent queries of this type
+            if hasattr(self, 'content_cache'):
+                return self.content_cache
+        
+        last_pos = 0
+        content = ""
+        for marker in self.iterate_markers(exclude):
+            content += self.content[last_pos:marker.begin]
+            content += " " * (marker.end - marker.begin)
+            last_pos = marker.end
+        content += self.content[last_pos:]
+
+        if exclude == (Marker.T.COMMENT | Marker.T.STRING | Marker.T.PREPROCESSOR):
+            self.content_cache = content
+        
+        assert(len(content) == len(self.content))
+        return content
+
+    def internal_append_region(self, region):
+        # here we apply some magic - we rely on special ordering of coming regions,
+        # which is supported by code parsers
+        prev_id = None
+        while True:
+            if len(self.loading_tmp) == 0:
+                break
+            prev_id = self.loading_tmp.pop()
+            if self.get_region(prev_id).get_offset_end() > region.get_offset_begin():
+                self.loading_tmp.append(prev_id) # return back
+                break
+        self.loading_tmp.append(region.get_id())
+        if prev_id != None:
+            self.get_region(prev_id).register_subregion_id(region.get_id())
+        self.regions.append(region)
+
+    def load_regions(self):
+        if self.regions == None:
+            self.regions = []
+            for each in self.loader.db.iterate_regions(self.get_id()):
+                self.internal_append_region(FileRegionData(self.loader,
+                                                   self.get_id(),
+                                                   each.region_id,
+                                                   each.name,
+                                                   each.begin,
+                                                   each.end,
+                                                   each.line_begin,
+                                                   each.line_end,
+                                                   each.cursor,
+                                                   each.group,
+                                                   each.checksum))
+                assert(len(self.regions) == each.region_id)
+        
+    def add_region(self, region_name, offset_begin, offset_end, line_begin, line_end, cursor_line, group, checksum):
+        if self.regions == None:
+            self.regions = [] # do not load in time of collection
+        new_id = len(self.regions) + 1
+        self.internal_append_region(FileRegionData(self.loader, self.get_id(), new_id, region_name, offset_begin, offset_end, line_begin, line_end, cursor_line, group, checksum))
+        self.loader.db.create_region(self.file_id, new_id, region_name, offset_begin, offset_end, line_begin, line_end, cursor_line, group, checksum)
+        return new_id
+        
+    def get_region(self, region_id):
+        self.load_regions()
+        return self.regions[region_id - 1]
+    
+    def iterate_regions(self, filter_group = FileRegionData.T.ANY):
+        self.load_regions()
+        for each in self.regions:
+            if each.group & filter_group:
+                yield each
+
+    def are_regions_loaded(self):
+        return self.regions != None
+
+    def load_markers(self):
+        if self.markers == None:
+            self.markers = []
+            for each in self.loader.db.iterate_markers(self.get_id()):
+                self.markers.append(Marker(each.begin, each.end, each.group))
+        
+    def add_marker(self, offset_begin, offset_end, group):
+        if self.markers == None:
+            self.markers = [] # do not load in time of collection
+        self.markers.append(Marker(offset_begin, offset_end, group))
+        self.loader.db.create_marker(self.file_id, offset_begin, offset_end, group)
+        
+    def iterate_markers(self, filter_group = Marker.T.COMMENT |
+                         Marker.T.STRING | Marker.T.PREPROCESSOR,
+                         region_id = None, exclude_children = True):
+        self.load_markers()
+        if region_id == None:
+            for each in self.markers:
+                if each.group & filter_group:
+                    yield each
+        else:
+            region = self.get_region(region_id)
+            if region != None:
+                if hasattr(region, 'markers_list') == False:
+                    def cache_markers_list_req(data, region_id, marker_start_pos):
+                        region = data.get_region(region_id)
+                        region.markers_list = []
+                        region.first_marker_pos = marker_start_pos
+                        for sub_id in region.iterate_subregion_ids():
+                            subregion = data.get_region(sub_id)
+                            while len(data.markers) > marker_start_pos and \
+                                subregion.get_offset_begin() > data.markers[marker_start_pos].get_offset_begin():
+                                    region.markers_list.append(marker_start_pos)
+                                    marker_start_pos += 1
+                            marker_start_pos = cache_markers_list_req(data, sub_id, marker_start_pos)
+                        while len(data.markers) > marker_start_pos and \
+                            region.get_offset_end() > data.markers[marker_start_pos].get_offset_begin():
+                                region.markers_list.append(marker_start_pos)
+                                marker_start_pos += 1
+                        return marker_start_pos
+                    next_marker_pos = cache_markers_list_req(self, 1, 0)
+                    assert(next_marker_pos == len(self.markers))
+                if exclude_children == True:
+                    for marker_pos in region.markers_list:
+                        marker = self.markers[marker_pos]
+                        if marker.group & filter_group:
+                            yield marker
+                elif len(self.markers) > region.first_marker_pos:
+                    for marker in self.markers[region.first_marker_pos:]:
+                        if marker.get_offset_begin() >= region.get_offset_end():
+                            break
+                        if region.get_offset_begin() > marker.get_offset_begin():
+                            continue
+                        if marker.group & filter_group:
+                            yield marker
+                        
+    def get_marker_types(self):
+        return Marker.T
+
+    def get_region_types(self):
+        return FileRegionData.T
+
+    def are_markers_loaded(self):
+        return self.markers != None
+
+    def __repr__(self):
+        return Data.__repr__(self) + " and regions " + self.regions.__repr__()
+
+class AggregatedData(Data):
+    
+    def __init__(self, loader, path):
+        Data.__init__(self)
+        self.path = path
+        self.loader = loader
+        self.subdirs = None
+        self.subfiles = None
+        
+    def get_subdirs(self):
+        if self.subdirs != None:
+            return self.subdirs
+        self.subdirs = []
+        if self.path != None:
+            for subdir in self.loader.db.iterate_dircontent(self.path, include_subdirs = True, include_subfiles = False):
+                self.subdirs.append(subdir)
+        return self.subdirs
+    
+    def get_subfiles(self):
+        if self.subfiles != None:
+            return self.subfiles
+        self.subfiles = []
+        if self.path != None:
+            for subfile in self.loader.db.iterate_dircontent(self.path, include_subdirs = False, include_subfiles = True):
+                self.subfiles.append(subfile)
+        return self.subfiles
+
+
+class SelectData(Data):
+
+    def __init__(self, loader, path, file_id, region_id):
+        Data.__init__(self)
+        self.loader = loader
+        self.path = path
+        self.file_id = file_id
+        self.region_id = region_id
+        self.region = None
+    
+    def get_path(self):
+        return self.path
+    
+    def get_region(self):
+        if self.region == None and self.region_id != None:
+            row = self.loader.db.get_region(self.file_id, self.region_id)
+            if row != None:
+                self.region = FileRegionData(self.loader,
+                                             self.file_id,
+                                             self.region_id,
+                                             row.name,
+                                             row.begin,
+                                             row.end,
+                                             row.line_begin,
+                                             row.line_end,
+                                             row.cursor,
+                                             row.group,
+                                             row.checksum)
+        return self.region
+
+
+class DiffData(Data):
+    
+    def __init__(self, new_data, old_data):
+        Data.__init__(self)
+        self.new_data = new_data
+        self.old_data = old_data
+    
+    def get_data(self, namespace, field):
+        new_data = self.new_data.get_data(namespace, field)
+        old_data = self.old_data.get_data(namespace, field)
+        if new_data == None:
+            return None
+        if old_data == None:
+            # non_zero fields has got zero value by default if missed
+            # the data can be also unavailable,
+            # because previous collection does not include that
+            # but external tools (like limit.py) should warn about this,
+            # using list of registered database properties
+            old_data = 0
+        return new_data - old_data
+
+####################################
+# Packager Interface
+####################################
+
+class PackagerError(Exception):
+    def __init__(self):
+        Exception.__init__(self, "Failed to pack or unpack.")
+
+class PackagerFactory(object):
+
+    def create(self, python_type, non_zero):
+        if python_type == None:
+            return PackagerFactory.SkipPackager()
+        if python_type == int:
+            if non_zero == False:
+                return PackagerFactory.IntPackager()
+            else:
+                return PackagerFactory.IntNonZeroPackager()
+        if python_type == float and non_zero == False:
+            return PackagerFactory.FloatPackager()
+        if python_type == str:
+            return PackagerFactory.StringPackager()
+        
+        class PackagerFactoryError(Exception):
+            def __init__(self, python_type):
+                Exception.__init__(self, "Python type '" + str(python_type) + "' is not supported by the factory.")
+        raise PackagerFactoryError(python_type)
+    
+    def get_python_type(self, sql_type):
+        if sql_type == "integer":
+            return int
+        if sql_type == "real":
+            return float
+        if sql_type == "text":
+            return str
+
+        class PackagerFactoryError(Exception):
+            def __init__(self, sql_type):
+                Exception.__init__(self, "SQL type '" + str(sql_type) + "' is not supported by the factory.")
+        raise PackagerFactoryError(sql_type)
+
+    class IPackager(object):
+        def pack(self, unpacked_data):
+            raise core.api.InterfaceNotImplemented(self)
+        def unpack(self, packed_data):
+            raise core.api.InterfaceNotImplemented(self)
+        def get_sql_type(self):
+            raise core.api.InterfaceNotImplemented(self)
+        def get_python_type(self):
+            raise core.api.InterfaceNotImplemented(self)
+        def is_non_zero(self):
+            return False
+        
+    class IntPackager(IPackager):
+        def pack(self, unpacked_data):
+            if not isinstance(unpacked_data, int):
+                raise PackagerError()
+            return str(unpacked_data)
+            
+        def unpack(self, packed_data): 
+            try:
+                return int(packed_data)
+            except ValueError:
+                raise PackagerError()
+    
+        def get_sql_type(self):
+            return "integer"
+        
+        def get_python_type(self):
+            return int
+    
+    class IntNonZeroPackager(IntPackager):
+        def pack(self, unpacked_data):
+            if unpacked_data == 0:
+                raise PackagerError()
+            return PackagerFactory.IntPackager.pack(self, unpacked_data)
+        def is_non_zero(self):
+            return True
+
+    class FloatPackager(IPackager):
+        def pack(self, unpacked_data):
+            if not isinstance(unpacked_data, float):
+                raise PackagerError()
+            return str(unpacked_data)
+            
+        def unpack(self, packed_data): 
+            try:
+                return float(packed_data)
+            except ValueError:
+                raise PackagerError()
+    
+        def get_sql_type(self):
+            return "real"
+
+        def get_python_type(self):
+            return float
+
+    class FloatNonZeroPackager(FloatPackager):
+        def pack(self, unpacked_data):
+            if unpacked_data == 0:
+                raise PackagerError()
+            return PackagerFactory.FloatPackager.pack(self, unpacked_data)
+        def is_non_zero(self):
+            return True
+
+    class StringPackager(IPackager):
+        def pack(self, unpacked_data):
+            if not isinstance(unpacked_data, str):
+                raise PackagerError()
+            return str(unpacked_data)
+            
+        def unpack(self, packed_data): 
+            try:
+                return str(packed_data)
+            except ValueError:
+                raise PackagerError()
+    
+        def get_sql_type(self):
+            return "text"
+
+        def get_python_type(self):
+            return str
+    
+    class SkipPackager(IPackager):
+        def pack(self, unpacked_data):
+            return None
+            
+        def unpack(self, packed_data): 
+            return None
+    
+        def get_sql_type(self):
+            return None
+            
+        def get_python_type(self):
+            return None
+            
+####################################
+# Loader
+####################################
+
+class NamespaceError(Exception):
+    def __init__(self, namespace, reason):
+        Exception.__init__(self, "Namespace '"
+                        + namespace 
+                        + "': '"
+                        + reason
+                        + "'")
+
+class FieldError(Exception):
+    def __init__(self, field, reason):
+        Exception.__init__(self, "Field '"
+                    + field 
+                    + "': '"
+                    + reason
+                    + "'")
+
+class Namespace(object):
+    
+    def __init__(self, db_handle, name, support_regions = False):
+        if not isinstance(name, str):
+            raise NamespaceError(name, "name not a string")
+        self.name = name
+        self.support_regions = support_regions
+        self.fields = {}
+        self.db = db_handle
+        
+        if self.db.check_table(name) == False:        
+            self.db.create_table(name, support_regions)
+        else:
+            for column in self.db.iterate_columns(name):
+                self.add_field(column.name, PackagerFactory().get_python_type(column.sql_type), non_zero=column.non_zero)
+        
+    def get_name(self):
+        return self.name
+
+    def are_regions_supported(self):
+        return self.support_regions
+    
+    def add_field(self, field_name, python_type, non_zero=False):
+        if not isinstance(field_name, str):
+            raise FieldError(field_name, "field_name not a string")
+        packager = PackagerFactory().create(python_type, non_zero)
+        if field_name in self.fields.keys():
+            raise FieldError(field_name, "double used")
+        self.fields[field_name] = packager
+        
+        if self.db.check_column(self.get_name(), field_name) == False:        
+            self.db.create_column(self.name, field_name, packager.get_sql_type(), non_zero=non_zero)
+    
+    def iterate_field_names(self):
+        for name in self.fields.keys():
+            yield name
+    
+    def get_field_packager(self, field_name):
+        if field_name in self.fields.keys():
+            return self.fields[field_name]
+        else:
+            return None
+        
+    def get_field_sql_type(self, field_name):
+        return self.get_field_packager(field_name).get_sql_type()
+
+    def get_field_python_type(self, field_name):
+        return self.get_field_packager(field_name).get_python_type()
+    
+class DataNotPackable(Exception):
+    def __init__(self, namespace, field, value, packager, extra_message):
+        Exception.__init__(self, "Data '"
+                           + str(value)
+                           + "' of type "
+                           + str(value.__class__) 
+                           + " referred by '"
+                           + namespace
+                           + "=>"
+                           + field
+                           + "' is not packable by registered packager '"
+                           + str(packager.__class__)
+                           + "': " + extra_message)
+
+class Loader(object):
+    
+    def __init__(self):
+        self.namespaces = {}
+        self.db = None
+        self.last_file_data = None # for performance boost reasons
+    
+    def create_database(self, dbfile, previous_db = None):
+        self.db = core.db.sqlite.Database()
+        if os.path.exists(dbfile):
+            logging.warn("Removing existing file: " + dbfile)
+            os.unlink(dbfile)
+        if previous_db != None and os.path.exists(previous_db) == False:
+            raise core.api.ExitError(None, "Database file '" + previous_db + "'  does not exist")
+
+        self.db.create(dbfile, clone_from=previous_db)
+        
+    def open_database(self, dbfile, read_only = True):
+        self.db = core.db.sqlite.Database()
+        if os.path.exists(dbfile) == False:
+            raise core.api.ExitError(None, "Database file '" + dbfile + "'  does not exist")
+        self.db.connect(dbfile, read_only=read_only)
+        
+        for table in self.db.iterate_tables():
+            self.create_namespace(table.name, table.support_regions)
+            
+    def set_property(self, property_name, value):
+        if self.db == None:
+            return None
+        return self.db.set_property(property_name, value)
+    
+    def get_property(self, property_name):
+        if self.db == None:
+            return None
+        return self.db.get_property(property_name)
+
+    def iterate_properties(self):
+        if self.db == None:
+            return None
+        return self.db.iterate_properties()
+            
+    def create_namespace(self, name, support_regions = False):
+        if self.db == None:
+            return None
+        
+        if name in self.namespaces.keys():
+            raise NamespaceError(name, "double used")
+        new_namespace = Namespace(self.db, name, support_regions)
+        self.namespaces[name] = new_namespace
+        return new_namespace
+    
+    def iterate_namespace_names(self):
+        for name in self.namespaces.keys():
+            yield name
+
+    def get_namespace(self, name):
+        if name in self.namespaces.keys():
+            return self.namespaces[name]
+        else:
+            return None
+
+    def create_file_data(self, path, checksum, content):
+        if self.db == None:
+            return None
+
+        (new_id, is_updated) = self.db.create_file(path, checksum)
+        result = FileData(self, path, new_id, checksum, content) 
+        self.last_file_data = result
+        return (result, is_updated)
+
+    def load_file_data(self, path):
+        if self.db == None:
+            return None
+
+        if self.last_file_data != None and self.last_file_data.get_path() == path:
+            return self.last_file_data
+        
+        data = self.db.get_file(path)
+        if data == None:
+            return None
+        
+        result = FileData(self, data.path, data.id, data.checksum, None)
+        self.last_file_data = result
+        return result
+
+    def save_file_data(self, file_data):
+        if self.db == None:
+            return None
+
+        class DataIterator(object):
+
+            def iterate_packed_values(self, data, namespace, support_regions = False):
+                for each in data.iterate_fields(namespace):
+                    space = self.loader.get_namespace(namespace)
+                    if space == None:
+                        raise DataNotPackable(namespace, each[0], each[1], None, "The namespace has not been found")
+                    
+                    packager = space.get_field_packager(each[0])
+                    if packager == None:
+                        raise DataNotPackable(namespace, each[0], each[1], None, "The field has not been found")
+        
+                    if space.support_regions != support_regions:
+                        raise DataNotPackable(namespace, each[0], each[1], packager, "Incompatible support for regions")
+                    
+                    try:
+                        packed_data = packager.pack(each[1])
+                        if packed_data == None:
+                            continue
+                    except PackagerError:
+                        raise DataNotPackable(namespace, each[0], each[1], packager, "Packager raised exception")
+                    
+                    yield (each[0], packed_data)
+            
+            def __init__(self, loader, data, namespace, support_regions = False):
+                self.loader = loader
+                self.iterator = self.iterate_packed_values(data, namespace, support_regions)
+    
+            def __iter__(self):
+                return self.iterator
+        
+        for namespace in file_data.iterate_namespaces():
+            if file_data.is_namespace_updated(namespace) == False:
+                continue
+            self.db.add_row(namespace,
+                            file_data.get_id(),
+                            None,
+                            DataIterator(self, file_data, namespace))
+        
+        if file_data.are_regions_loaded():
+            for region in file_data.iterate_regions():
+                for namespace in region.iterate_namespaces():
+                    if region.is_namespace_updated(namespace) == False:
+                        continue
+                    self.db.add_row(namespace,
+                                    file_data.get_id(),
+                                    region.get_id(),
+                                    DataIterator(self, region, namespace, support_regions = True))
+
+    def iterate_file_data(self, path = None, path_like_filter = "%"):
+        if self.db == None:
+            return None
+        
+        final_path_like = path_like_filter
+        if path != None:
+            if self.db.check_dir(path) == False and self.db.check_file(path) == False:
+                return None
+            final_path_like = path + path_like_filter
+
+        class FileDataIterator(object):
+            def iterate_file_data(self, loader, final_path_like):
+                for data in loader.db.iterate_files(path_like=final_path_like):
+                    yield FileData(loader, data.path, data.id, data.checksum, None)
+            
+            def __init__(self, loader, final_path_like):
+                self.iterator = self.iterate_file_data(loader, final_path_like)
+    
+            def __iter__(self):
+                return self.iterator
+
+        if self.db == None:
+            return None
+        return FileDataIterator(self, final_path_like)
+
+    def load_aggregated_data(self, path = None, path_like_filter = "%", namespaces = None):
+        if self.db == None:
+            return None
+
+        final_path_like = path_like_filter
+        if path != None:
+            if self.db.check_dir(path) == False and self.db.check_file(path) == False:
+                return None
+            final_path_like = path + path_like_filter
+        
+        if namespaces == None:
+            namespaces = self.namespaces.keys()
+        
+        result = AggregatedData(self, path)
+        for name in namespaces:
+            namespace = self.get_namespace(name)
+            data = self.db.aggregate_rows(name, path_like = final_path_like)
+            for field in data.keys():
+                if namespace.get_field_packager(field).is_non_zero() == True:
+                    data[field]['min'] = None
+                    data[field]['avg'] = None
+                result.set_data(name, field, data[field])
+        
+        return result
+    
+    def load_selected_data(self, namespace, fields = None, path = None, path_like_filter = "%", filters = []):
+        if self.db == None:
+            return None
+        
+        # TODO implement restriction for non_zero fields
+
+        final_path_like = path_like_filter
+        if path != None:
+            if self.db.check_dir(path) == False and self.db.check_file(path) == False:
+                return None
+            final_path_like = path + path_like_filter
+        
+        namespace_obj = self.get_namespace(namespace)
+        if namespace_obj == None:
+            return None
+        
+        class SelectDataIterator(object):
+        
+            def iterate_selected_values(self, loader, namespace_obj, final_path_like, fields, filters):
+                for row in loader.db.select_rows(namespace_obj.get_name(), path_like=final_path_like, filters=filters):
+                    region_id = None
+                    if namespace_obj.are_regions_supported() == True:
+                        region_id = row['region_id']
+                    data = SelectData(loader, row['path'], row['id'], region_id)
+                    field_names = fields
+                    if fields == None:
+                        field_names = namespace_obj.iterate_field_names()
+                    for field in field_names:
+                        data.set_data(namespace, field, row[field])
+                    yield data
+            
+            def __init__(self, loader, namespace_obj, final_path_like, fields, filters):
+                self.iterator = self.iterate_selected_values(loader, namespace_obj, final_path_like, fields, filters)
+    
+            def __iter__(self):
+                return self.iterator
+
+        return SelectDataIterator(self, namespace_obj, final_path_like, fields, filters)
+    

+ 52 - 0
releases/metrixplusplus-1.1.70/core/db/post.py

@@ -0,0 +1,52 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import core.api
+
+import os.path
+import re
+
+class Plugin(core.api.Plugin, core.api.IConfigurable):
+    
+    def declare_configuration(self, parser):
+        parser.add_option("--general.db-file", default='./metrixpp.db',
+                         help="Primary database file to write (by the collector) and post-process (by other tools) [default: %default]")
+        parser.add_option("--general.db-file-prev", default=None,
+                         help="Database file with data collected for the past/previous revision."
+                             " If it is set for the collector tool to perform an incremental/iterative collection,"
+                             " it may reduce the processing time significantly."
+                             " Post-processing tools use it in order to recognise/evaluate change trends. [default: %default].")
+    
+    def configure(self, options):
+        self.dbfile = options.__dict__['general.db_file']
+        self.dbfile_prev = options.__dict__['general.db_file_prev']
+        
+    def initialize(self):
+        
+        self.get_plugin_loader().get_database_loader().create_database(self.dbfile, previous_db = self.dbfile_prev)    
+        
+        # do not process files dumped by this module
+        self.get_plugin_loader().get_plugin('core.dir').add_exclude_rule(re.compile(r'^' + os.path.basename(self.dbfile) + r'$'))
+        if self.dbfile_prev != None:
+            self.get_plugin_loader().get_plugin('core.dir').add_exclude_rule(re.compile(r'^' + os.path.basename(self.dbfile_prev) + r'$'))
+        
+        
+
+
+    

+ 639 - 0
releases/metrixplusplus-1.1.70/core/db/sqlite.py

@@ -0,0 +1,639 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import sqlite3
+import re
+import os.path
+import logging
+import itertools 
+import shutil
+
+class Database(object):
+    
+    last_used_id = 0
+    version = "1.0"
+    
+    class TableData(object):
+        def __init__(self, table_id, name, support_regions):
+            self.id = table_id
+            self.name = name
+            self.support_regions = support_regions
+    
+    class ColumnData(object):
+        def __init__(self, column_id, name, sql_type, non_zero):
+            self.id = column_id
+            self.name = name
+            self.sql_type = sql_type
+            self.non_zero = non_zero
+
+    class TagData(object):
+        def __init__(self, tag_id, name):
+            self.id = tag_id
+            self.name = name
+
+    class PropertyData(object):
+        def __init__(self, property_id, name, value):
+            self.id = property_id
+            self.name = name
+            self.value = value
+
+    class FileData(object):
+        def __init__(self, file_id, path, checksum):
+            self.id = file_id
+            self.path = path
+            self.checksum = checksum
+
+    class RegionData(object):
+        def __init__(self, file_id, region_id, name, begin, end, line_begin, line_end, cursor, group, checksum):
+            self.file_id = file_id
+            self.region_id = region_id
+            self.name = name
+            self.begin = begin
+            self.end = end
+            self.line_begin = line_begin
+            self.line_end = line_end
+            self.cursor = cursor
+            self.group = group
+            self.checksum = checksum
+
+    class MarkerData(object):
+        def __init__(self, file_id, begin, end, group):
+            self.file_id = file_id
+            self.begin = begin
+            self.end = end
+            self.group = group
+
+    def __init__(self):
+        self.read_only = False
+        self.conn = None
+        self.dirs = None
+        self.is_cloned = False
+        
+        self.last_used_id += 1
+        self.id = self.last_used_id
+    
+    def __del__(self):
+        if self.conn != None:
+            if self.is_cloned == True:
+                logging.debug("Cleaning up database file")
+                self.InternalCleanUpUtils().clean_up_not_confirmed(self)
+            logging.debug("Committing database file")
+            self.conn.commit()
+    
+    class InternalCleanUpUtils(object):
+        
+        def clean_up_not_confirmed(self, db_loader):
+            sql = "DELETE FROM __info__ WHERE (confirmed = 0)"
+            db_loader.log(sql)
+            db_loader.conn.execute(sql)
+            sql = "DELETE FROM __tags__ WHERE (confirmed = 0)"
+            db_loader.log(sql)
+            db_loader.conn.execute(sql)
+
+            sql = "SELECT * FROM __tables__ WHERE (confirmed = 0)"
+            db_loader.log(sql)
+            for table in db_loader.conn.execute(sql).fetchall():
+                sql = "DELETE FROM __columns__ WHERE table_id = '" + str(table['id']) + "'"
+                db_loader.log(sql)
+                db_loader.conn.execute(sql)
+                sql = "DELETE FROM __tables__ WHERE id = '" + str(table['id']) + "'"
+                db_loader.log(sql)
+                db_loader.conn.execute(sql)
+                sql = "DROP TABLE '" + table['name'] + "'"
+                db_loader.log(sql)
+                db_loader.conn.execute(sql)
+
+            sql = "SELECT __columns__.name AS column_name, __tables__.name AS table_name, __columns__.id AS column_id FROM __columns__, __tables__ WHERE (__columns__.confirmed = 0 AND __columns__.table_id = __tables__.id)"
+            db_loader.log(sql)
+            for column in db_loader.conn.execute(sql).fetchall():
+                logging.warn("New database file inherits useless column: '" + column['table_name'] + "'.'" + column['column_name'] + "'")
+                sql = "DELETE FROM __columns__ WHERE id = '" + str(column['column_id']) + "'"
+                db_loader.log(sql)
+                db_loader.conn.execute(sql)
+                sql = "UPDATE '" + column['table_name'] + "' SET '" + column['column_name'] + "' = NULL"
+                db_loader.log(sql)
+                db_loader.conn.execute(sql)
+            
+            self.clean_up_file(db_loader)
+
+        def clean_up_file(self, db_loader, file_id = None):
+            sql = "SELECT * FROM __tables__"
+            db_loader.log(sql)
+            for table in itertools.chain(db_loader.conn.execute(sql).fetchall(), [{'name':'__regions__'}, {'name':'__markers__'}]):
+                sql = ""
+                if file_id == None:
+                    sql = "DELETE FROM '" + table['name'] + "' WHERE file_id IN (SELECT __files__.id FROM __files__ WHERE __files__.confirmed = 0)"
+                else:
+                    sql = "DELETE FROM '" + table['name'] + "' WHERE (file_id = " + str(file_id) + ")"
+                db_loader.log(sql)
+                db_loader.conn.execute(sql)
+            
+    class InternalPathUtils(object):
+        
+        def iterate_heads(self, path):
+            dirs = []
+            head = os.path.dirname(path)
+            last_head = None # to process Windows drives
+            while (head != "" and last_head != head):
+                dirs.append(os.path.basename(head))
+                last_head = head
+                head = os.path.dirname(head)
+            dirs.reverse()
+            for each in dirs:
+                yield each
+                
+        def normalize_path(self, path):
+            if path == None:
+                return None
+            return re.sub(r'''[\\]''', "/", path)
+        
+        def update_dirs(self, db_loader, path = None):
+            if db_loader.dirs == None:
+                if path == None:
+                    db_loader.dirs = {} # initial construction
+                else:
+                    return # avoid useless cache updates 
+            elif path == None:
+                return # avoid multiple initial constructions
+            
+            path = self.normalize_path(path)
+            rows = None
+            if path == None:
+                sql = "SELECT * FROM __files__"
+                db_loader.log(sql)
+                rows = db_loader.conn.execute(sql).fetchall()
+            else:
+                rows = [{"path": path}]
+            for row in rows:
+                cur_head = db_loader.dirs
+                for dir_name in self.iterate_heads(row["path"]):
+                    if dir_name not in cur_head.keys():
+                        cur_head[dir_name] = {}
+                    cur_head = cur_head[dir_name]
+                cur_head[os.path.basename(row["path"])] = None
+
+
+    def create(self, file_name, clone_from = None):
+        if clone_from != None:
+            self.is_cloned = True
+            logging.debug("Cloning database file: " + clone_from)
+            shutil.copy2(clone_from, file_name)
+            logging.debug("Connecting database file: " + file_name)
+            self.conn = sqlite3.connect(file_name)
+            self.conn.row_factory = sqlite3.Row
+            self.read_only = False
+            
+            sql = "UPDATE __tables__ SET confirmed = 0"
+            self.log(sql)
+            self.conn.execute(sql)
+            sql = "UPDATE __columns__ SET confirmed = 0"
+            self.log(sql)
+            self.conn.execute(sql)
+            sql = "UPDATE __tags__ SET confirmed = 0"
+            self.log(sql)
+            self.conn.execute(sql)
+            sql = "UPDATE __files__ SET confirmed = 0"
+            self.log(sql)
+            self.conn.execute(sql)
+                
+        else:
+            self.connect(file_name)
+        
+    def connect(self, file_name, read_only = False):
+        logging.debug("Connecting database file: " + file_name)
+        self.conn = sqlite3.connect(file_name)
+        self.conn.row_factory = sqlite3.Row
+        self.read_only = read_only
+        if self.read_only == False:
+            try:
+                sql = "CREATE TABLE __info__ (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, property text NOT NULL, value text, confirmed integer NOT NULL, UNIQUE (property) ON CONFLICT REPLACE)"
+                self.log(sql)
+                self.conn.execute(sql)
+                sql = "INSERT INTO __info__ (property, value, confirmed) VALUES ('version', '" + self.version + "', 1)"
+                self.log(sql)
+                self.conn.execute(sql)
+                sql = "CREATE TABLE __tables__ (id integer NOT NULL PRIMARY KEY, name text NOT NULL, support_regions integer NOT NULL, confirmed integer NOT NULL, UNIQUE (name))"
+                self.log(sql)
+                self.conn.execute(sql)
+                sql = "CREATE TABLE __columns__ (id integer NOT NULL PRIMARY KEY, name text NOT NULL, type text NOT NULL, table_id integer NOT_NULL, non_zero integer NOT NULL, confirmed integer NOT NULL, UNIQUE (name, table_id))"
+                self.log(sql)
+                self.conn.execute(sql)
+                sql = "CREATE TABLE __tags__ (id integer NOT NULL PRIMARY KEY, name text NOT NULL UNIQUE, confirmed integer NOT NULL)"
+                self.log(sql)
+                self.conn.execute(sql)
+                sql = "CREATE TABLE __files__ (id integer NOT NULL PRIMARY KEY AUTOINCREMENT, path text NOT NULL, checksum integer NOT NULL, tag1 integer, tag2 integer, tag3 integer, confirmed integer NOT NULL, UNIQUE(path))"
+                self.log(sql)
+                self.conn.execute(sql)
+                sql = "CREATE TABLE __regions__ (file_id integer NOT NULL, region_id integer NOT NULL, name text NOT NULL, begin integer NOT NULL, end integer NOT NULL, line_begin integer NOT NULL, line_end integer NOT NULL, cursor integer NOT NULL, group_id integer NOT NULL, checksum integer NOT NULL, PRIMARY KEY (file_id, region_id))"
+                self.log(sql)
+                self.conn.execute(sql)
+                sql = "CREATE TABLE __markers__ (id integer NOT NULL PRIMARY KEY, file_id integer NOT NULL, begin integer NOT NULL, end integer NOT NULL, group_id integer NOT NULL)"
+                self.log(sql)
+                self.conn.execute(sql)
+            except sqlite3.OperationalError as e:
+                logging.debug("sqlite3.OperationalError: " + str(e))
+                
+    def set_property(self, property_name, value):
+        ret_val = None
+        sql = "SELECT * FROM __info__ WHERE (property = '" + property_name + "')"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        if len(result) != 0:
+            ret_val = result[0]['value']
+
+        sql = "INSERT INTO __info__ (property, value, confirmed) VALUES ('" + property_name + "', '" + value + "', 1)"
+        self.log(sql)
+        self.conn.execute(sql)
+        return ret_val
+        
+    def get_property(self, property_name):
+        ret_val = None
+        sql = "SELECT * FROM __info__ WHERE (property = '" + property_name + "' AND confirmed = 1)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        if len(result) != 0:
+            ret_val = result[0]['value']
+        return ret_val
+
+    def iterate_properties(self):
+        sql = "SELECT * FROM __info__ WHERE (confirmed = 1)"
+        self.log(sql)
+        for each in self.conn.execute(sql).fetchall():
+            yield self.PropertyData(each['id'], each['property'], each['value'])
+
+    def create_table(self, table_name, support_regions = False):
+        assert(self.read_only == False)
+
+        sql = "SELECT * FROM __tables__ WHERE (name = '" + table_name + "'AND confirmed == 0)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        if len(result) != 0:
+            sql = "UPDATE __tables__ SET confirmed = 1 WHERE (name = '" + table_name + "')"
+            self.log(sql)
+            self.conn.execute(sql)
+            return        
+        
+        sql = "CREATE TABLE '" + table_name + "' (file_id integer NOT NULL PRIMARY KEY)"
+        if support_regions == True:
+            sql = str("CREATE TABLE '" + table_name + "' (file_id integer NOT NULL, region_id integer NOT NULL, "
+                      + "PRIMARY KEY (file_id, region_id))")
+            
+        self.log(sql)
+        self.conn.execute(sql)
+        sql = "INSERT INTO __tables__ (name, support_regions, confirmed) VALUES ('" + table_name + "', '" + str(int(support_regions)) + "', 1)"
+        self.log(sql)
+        self.conn.execute(sql)
+        
+    def iterate_tables(self):
+        sql = "SELECT * FROM __tables__ WHERE (confirmed = 1)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        for row in result:
+            yield self.TableData(int(row["id"]), str(row["name"]), bool(row["support_regions"]))
+            
+    def check_table(self, table_name):
+        sql = "SELECT * FROM __tables__ WHERE (name = '" + table_name + "' AND confirmed = 1)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        if len(result) == 0:
+            return False
+        return True
+
+    def create_column(self, table_name, column_name, column_type, non_zero=False):
+        assert(self.read_only == False)
+        if column_type == None:
+            logging.debug("Skipping column '" + column_name + "' creation for table '" + table_name + "'")
+            return
+        
+        sql = "SELECT id FROM __tables__ WHERE (name = '" + table_name + "')"
+        self.log(sql)
+        table_id = self.conn.execute(sql).next()['id']
+
+        sql = "SELECT * FROM __columns__ WHERE (table_id = '" + str(table_id) + "' AND name = '" + column_name + "' AND confirmed == 0)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        if len(result) != 0:
+            sql = "UPDATE __columns__ SET confirmed = 1 WHERE (table_id = '" + str(table_id) + "' AND name = '" + column_name + "')"
+            self.log(sql)
+            self.conn.execute(sql)
+            return        
+        
+        sql = "ALTER TABLE '" + table_name + "' ADD COLUMN '" + column_name + "' " + column_type
+        self.log(sql)
+        self.conn.execute(sql)
+        sql = "SELECT id FROM __tables__ WHERE (name = '" + table_name + "')"
+        self.log(sql)
+        table_id = self.conn.execute(sql).next()['id']
+        sql = "INSERT INTO __columns__ (name, type, table_id, non_zero, confirmed) VALUES ('" + column_name + "', '" + column_type + "', '" + str(table_id) + "', '" + str(int(non_zero)) + "', 1)"
+        self.log(sql)
+        self.conn.execute(sql)        
+
+    def iterate_columns(self, table_name):
+        sql = "SELECT id FROM __tables__ WHERE (name = '" + table_name + "')"
+        self.log(sql)
+        table_id = self.conn.execute(sql).next()['id']
+        sql = "SELECT * FROM __columns__ WHERE (table_id = '" + str(table_id) + "' AND confirmed = 1)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        for row in result:
+            yield self.ColumnData(int(row["id"]), str(row["name"]), str(row["type"]), bool(row["non_zero"]))
+
+    def check_column(self, table_name, column_name):
+        sql = "SELECT id FROM __tables__ WHERE (name = '" + table_name + "')"
+        self.log(sql)
+        table_id = self.conn.execute(sql).next()['id']
+        sql = "SELECT * FROM __columns__ WHERE (table_id = '" + str(table_id) + "' AND name = '" + column_name + "' AND confirmed = 1)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        if len(result) == 0:
+            return False
+        return True
+    
+    def create_tag(self, tag_name):
+        assert(self.read_only == False)
+        
+        sql = "SELECT * FROM __tags__ WHERE (name = '" + tag_name + "' AND confirmed == 0)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        if len(result) != 0:
+            sql = "UPDATE __tags__ SET confirmed = 1 WHERE (name = '" + tag_name + "')"
+            self.log(sql)
+            self.conn.execute(sql)
+            return        
+        
+        sql = "INSERT INTO __tags__ (name, confirmed) VALUES ('" + tag_name + "', 1)"
+        self.log(sql)
+        self.conn.execute(sql)        
+
+    def iterate_tags(self):
+        sql = "SELECT * FROM __tags__ WHERE (confirmed = 1)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        for row in result:
+            yield self.TagData(int(row["id"]), str(row["name"]))
+
+    def check_tag(self, tag_name):
+        sql = "SELECT * FROM __tags__ WHERE (name = '" + tag_name + "' AND confirmed = 1)"
+        self.log(sql)
+        result = self.conn.execute(sql).fetchall()
+        if len(result) == 0:
+            return False
+        return True
+
+    # TODO activate usage of tags
+    def create_file(self, path, checksum):
+        assert(self.read_only == False)
+        path = self.InternalPathUtils().normalize_path(path)
+
+        if self.is_cloned == True:
+            sql = "SELECT * FROM __files__ WHERE (path = '" + path + "')"
+            self.log(sql)
+            result = self.conn.execute(sql).fetchall()
+            if len(result) != 0:
+                if result[0]['checksum'] == checksum:
+                    old_id = result[0]['id']
+                    sql = "UPDATE __files__ SET confirmed = 1 WHERE (id = " + str(old_id) +")"
+                    self.log(sql)
+                    self.conn.execute(sql)
+                    return (old_id, False)
+                else:
+                    self.InternalCleanUpUtils().clean_up_file(self, result[0]['id'])
+        
+        sql = "INSERT OR REPLACE INTO __files__ (path, checksum, confirmed) VALUES (?, ?, 1)"
+        column_data = [path, checksum]
+        self.log(sql + " /with arguments: " + str(column_data))
+        cur = self.conn.cursor()
+        cur.execute(sql, column_data)
+        self.InternalPathUtils().update_dirs(self, path=path)
+        return (cur.lastrowid, True)
+    
+    def iterate_dircontent(self, path, include_subdirs = True, include_subfiles = True):
+        self.InternalPathUtils().update_dirs(self)
+        path = self.InternalPathUtils().normalize_path(path)
+        cur_head = self.dirs
+        valid = True
+        if path != "":
+            for head in self.InternalPathUtils().iterate_heads(path):
+                if head not in cur_head.keys():
+                    # non existing directory
+                    valid = False
+                else:
+                    cur_head = cur_head[head]
+            basename = os.path.basename(path)
+            if basename not in cur_head.keys() or cur_head[basename] == None:
+                # do not exist or points to the file
+                valid = False
+            else:
+                cur_head = cur_head[basename]
+        if valid == True:
+            for elem in cur_head.keys():
+                if include_subdirs == True and cur_head[elem] != None:
+                    yield elem
+                if include_subfiles == True and cur_head[elem] == None:
+                    yield elem
+
+    def check_file(self, path):
+        return self.get_file(path) != None
+
+    def check_dir(self, path):
+        for each in self.iterate_dircontent(path):
+            each = each # used
+            return True # there is at least one item
+        return False
+
+    def get_file(self, path):
+        path = self.InternalPathUtils().normalize_path(path)
+        result = self.select_rows("__files__", filters = [("path", "=", path), ("confirmed", "=", 1)])
+        if len(result) == 0:
+            return None
+        assert(len(result) == 1)
+        return self.FileData(result[0]['id'], result[0]['path'], result[0]['checksum'])
+
+    def iterate_files(self, path_like = None):
+        for row in self.select_rows('__files__', path_like=path_like, filters=[('confirmed','=','1')]): 
+            yield self.FileData(row['id'], row['path'], row['checksum'])
+
+    def create_region(self, file_id, region_id, name, begin, end, line_begin, line_end, cursor, group, checksum):
+        assert(self.read_only == False)
+        sql = "INSERT OR REPLACE INTO __regions__ (file_id, region_id, name, begin, end, line_begin, line_end, cursor, group_id, checksum) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
+        column_data = [file_id, region_id, name, begin, end, line_begin, line_end, cursor, group, checksum]
+        self.log(sql + " /with arguments: " + str(column_data))
+        cur = self.conn.cursor()
+        cur.execute(sql, column_data)
+        return cur.lastrowid
+    
+    def get_region(self, file_id, region_id):
+        result = self.select_rows("__regions__", filters = [("file_id", "=", file_id), ("region_id", "=", region_id)])
+        if len(result) == 0:
+            return None
+        return self.RegionData(result[0]['file_id'],
+                               result[0]['region_id'],
+                               result[0]['name'],
+                               result[0]['begin'],
+                               result[0]['end'],
+                               result[0]['line_begin'],
+                               result[0]['line_end'],
+                               result[0]['cursor'],
+                               result[0]['group_id'],
+                               result[0]['checksum'])
+
+    def iterate_regions(self, file_id):
+        for each in self.select_rows("__regions__", filters = [("file_id", "=", file_id)]):
+            yield self.RegionData(each['file_id'],
+                                  each['region_id'],
+                                  each['name'],
+                                  each['begin'],
+                                  each['end'],
+                                  each['line_begin'],
+                                  each['line_end'],
+                                  each['cursor'],
+                                  each['group_id'],
+                                  each['checksum'])
+    
+    def create_marker(self, file_id, begin, end, group):
+        assert(self.read_only == False)
+        sql = "INSERT OR REPLACE INTO __markers__ (file_id, begin, end, group_id) VALUES (?, ?, ?, ?)"
+        column_data = [file_id, begin, end, group]
+        self.log(sql + " /with arguments: " + str(column_data))
+        cur = self.conn.cursor()
+        cur.execute(sql, column_data)
+        return cur.lastrowid
+    
+    def iterate_markers(self, file_id):
+        for each in self.select_rows("__markers__", filters = [("file_id", "=", file_id)]):
+            yield self.MarkerData(each['file_id'],
+                                  each['begin'],
+                                  each['end'],
+                                  each['group_id'])
+
+    def add_row(self, table_name, file_id, region_id, array_data):
+        assert(self.read_only == False)
+        column_names = "'file_id'"
+        column_values = "?"
+        column_data = [file_id]
+        if region_id != None:
+            column_names += ", 'region_id'"
+            column_values += ", ?"
+            column_data.append(region_id)
+        useful_data = 0
+        for each in array_data:
+            column_names +=  ", '" + each[0] + "'"
+            column_values += ", ?"
+            column_data.append(each[1])
+            useful_data += 1
+        if useful_data == 0:
+            return
+        sql = "INSERT OR REPLACE INTO '" + table_name + "' (" + column_names + ") VALUES (" + column_values + ")"
+        self.log(sql + " /with arguments: " + str(column_data))
+        cur = self.conn.cursor()
+        cur.execute(sql, column_data)
+        return cur.lastrowid
+
+    def select_rows(self, table_name, path_like = None, column_names = [], filters = []):
+        safe_column_names = []
+        for each in column_names:
+            safe_column_names.append("'" + each + "'")
+        return self.select_rows_unsafe(table_name, path_like = path_like, column_names = safe_column_names, filters = filters)
+
+    def select_rows_unsafe(self, table_name, path_like = None, column_names = [], filters = []):
+        path_like = self.InternalPathUtils().normalize_path(path_like)
+        if self.conn == None:
+            return []
+
+        table_stmt = "'" + table_name + "'"
+
+        what_stmt = ", ".join(column_names)
+        if len(what_stmt) == 0:
+            what_stmt = "*"
+        elif path_like != None and table_name != '__files__':
+            what_stmt += ", '__files__'.'path', '__files__'.'id'"
+        inner_stmt = ""
+        if path_like != None and table_name != '__files__':
+            inner_stmt = " INNER JOIN '__files__' ON '__files__'.'id' = '" + table_name + "'.'file_id' "
+
+        where_stmt = " "
+        values = ()
+        if len(filters) != 0:
+            if filters[0][1] == 'IN':
+                where_stmt = " WHERE (`" + filters[0][0] + "` " + filters[0][1] + " " + filters[0][2]
+            else:    
+                where_stmt = " WHERE (`" + filters[0][0] + "` " + filters[0][1] + " ?"
+                values = (filters[0][2],)
+            for each in filters[1:]:
+                if each[1] == 'IN':
+                    where_stmt += " AND `" + each[0] + "` " + each[1] + " " + each[2]
+                else:
+                    where_stmt += " AND `" + each[0] + "` " + each[1] + " ?"
+                    values += (each[2], )
+            if path_like != None:
+                where_stmt += " AND '__files__'.'path' LIKE ?"
+                values += (path_like, )
+            where_stmt += ")"
+        else:
+            where_stmt = " WHERE '__files__'.'path' LIKE ?"
+            values += (path_like, )
+
+        sql = "SELECT " + what_stmt + " FROM " + table_stmt + inner_stmt + where_stmt
+        self.log(sql + " /with arguments: " + str(values))
+        return self.conn.execute(sql, values).fetchall()
+
+    def get_row(self, table_name, file_id, region_id):
+        selected = self.get_rows(table_name, file_id, region_id)
+        # assures that only one row in database
+        # if assertion happens, caller's intention is not right, use get_rows instead    
+        assert(len(selected) == 0 or len(selected) == 1)
+        if len(selected) == 0:
+            return None
+        return selected[0]
+
+    def get_rows(self, table_name, file_id, region_id):
+        filters = [("file_id", '=', file_id)]
+        if region_id != None:
+            filters.append(("region_id", '=', region_id))
+        return self.select_rows(table_name, filters=filters)
+    
+    def aggregate_rows(self, table_name, path_like = None, column_names = None, filters = []):
+        
+        if column_names == None:
+            column_names = []
+            for column in self.iterate_columns(table_name):
+                column_names.append(column.name)
+                
+        if len(column_names) == 0:
+            # it is possible that a table does not have meanfull columns
+            return {} 
+        
+        total_column_names = []
+        for column_name in column_names:
+            for func in ['max', 'min', 'avg', 'total']:
+                total_column_names.append(func + "('" + table_name + "'.'" + column_name + "') AS " + "'" + column_name + "_" + func + "'")
+             
+        data = self.select_rows_unsafe(table_name, path_like = path_like, column_names = total_column_names, filters = filters)
+        assert(len(data) == 1)
+        result = {}
+        for column_name in column_names:
+            result[column_name] = {}
+            for func in ['max', 'min', 'avg', 'total']:
+                result[column_name][func] = data[0][column_name + "_" + func]
+        return result
+    
+    def log(self, sql):
+        #import traceback
+        #traceback.print_stack()
+        logging.debug("[" + str(self.id) + "] Executing query: " + sql)

+ 93 - 0
releases/metrixplusplus-1.1.70/core/db/utils.py

@@ -0,0 +1,93 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+class FileRegionsDisposableGetter(object):
+    
+    def __init__(self, file_data):
+        self.checksums = {}
+        self.names = {}
+        
+        for each in file_data.iterate_regions():
+            if each.get_checksum() not in self.checksums:
+                self.checksums[each.get_checksum()] = []
+            self.checksums[each.get_checksum()].append((each.get_id(), each.get_name())) 
+            
+            if each.get_name() not in self.names:
+                self.names[each.get_name()] = []
+            self.names[each.get_name()].append((each.get_id(), each.get_checksum())) 
+        
+    def get_next_id_once_by_checksum(self, checksum):
+        if checksum not in self.checksums.keys():
+            return None
+
+        if len(self.checksums[checksum]) == 0:
+            return None
+        
+        elem = self.checksums[checksum].pop(0)
+        next_id = elem[0]
+        next_name = elem[1]
+
+        self.names[next_name].remove((next_id, checksum))
+        return next_id
+
+    def get_next_id_once_by_name(self, name):
+        if name not in self.names.keys():
+            return None
+        
+        if len(self.names[name]) == 0:
+            return None
+        
+        elem = self.names[name].pop(0)
+        next_id = elem[0]
+        next_checksum = elem[1]
+
+        self.checksums[next_checksum].remove((next_id, name))
+        return next_id
+    
+class FileRegionsMatcher(object):
+    
+    def __init__(self, file_data, prev_file_data):
+        self.ids = [None] # add one to shift id from zero
+        
+        once_filter = FileRegionsDisposableGetter(prev_file_data)
+        unmatched_region_ids = []
+        for (ind, region) in enumerate(file_data.iterate_regions()):
+            assert(ind + 1 == region.get_id())
+            # Identify corresponding region in previous database (attempt by checksum)
+            prev_id = once_filter.get_next_id_once_by_checksum(region.checksum)
+            if prev_id != None:
+                self.ids.append((prev_id, False))
+            else:
+                unmatched_region_ids.append(region.get_id())
+                self.ids.append((None, True))
+                            
+        # Identify corresponding region in previous database (attempt by name)
+        for region_id in unmatched_region_ids: 
+            prev_id = once_filter.get_next_id_once_by_name(file_data.get_region(region_id).name)
+            if prev_id != None:
+                self.ids[region_id] = (prev_id, True)
+    
+    def get_prev_id(self, curr_id):
+        return self.ids[curr_id][0]
+
+    def is_matched(self, curr_id):
+        return (self.ids[curr_id][0] != None)
+
+    def is_modified(self, curr_id):
+        return self.ids[curr_id][1]

+ 119 - 0
releases/metrixplusplus-1.1.70/core/dir.py

@@ -0,0 +1,119 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import core.api
+
+import re
+import os
+import logging
+import time
+import binascii
+
+class Plugin(core.api.Plugin, core.api.Parent, core.api.IConfigurable, core.api.IRunable):
+    
+    def __init__(self):
+        self.reader = DirectoryReader()
+        self.exclude_rules = []
+    
+    def declare_configuration(self, parser):
+        parser.add_option("--general.non-recursively", action="store_true", default=False,
+                         help="If the option is set (True), sub-directories are not processed [default: %default]")
+        parser.add_option("--general.exclude-files", default=r'^[.]',
+                         help="Defines the pattern to exclude files from processing [default: %default]")
+        parser.add_option("--general.proctime-on", action="store_true", default=False,
+                         help="If the option is set (True), the tool measures processing time per file [default: %default]")
+        parser.add_option("--general.procerrors-on", action="store_true", default=False,
+                         help="If the option is set (True), the tool counts number of processing/parsing errors per file [default: %default]")
+    
+    def configure(self, options):
+        self.non_recursively = options.__dict__['general.non_recursively']
+        self.add_exclude_rule(re.compile(options.__dict__['general.exclude_files']))
+        self.is_proctime_enabled = options.__dict__['general.proctime_on']
+        self.is_procerrors_enabled = options.__dict__['general.procerrors_on']
+
+    def initialize(self):
+        namespace = self.get_plugin_loader().get_database_loader().create_namespace('general')
+        if self.is_proctime_enabled == True:
+            namespace.add_field('proctime', float)
+        if self.is_procerrors_enabled == True:
+            namespace.add_field('procerrors', int)
+        
+    def run(self, args):
+        if len(args) == 0:
+            return self.reader.run(self, "./")
+        for directory in args:
+            return self.reader.run(self, directory)
+        
+    def add_exclude_rule(self, re_compiled_pattern):
+        # TODO file name may have special regexp symbols what causes an exception
+        # For example try to run a collection with "--general.db-file=metrix++" option
+        self.exclude_rules.append(re_compiled_pattern)
+        
+    def is_file_excluded(self, file_name):
+        for each in self.exclude_rules:
+            if re.match(each, file_name) != None:
+                return True
+        return False 
+        
+class DirectoryReader():
+    
+    def run(self, plugin, directory):
+        
+        def run_recursively(plugin, directory):
+            exit_code = 0
+            for fname in os.listdir(directory):
+                full_path = os.path.join(directory, fname)
+                norm_path = re.sub(r'''[\\]''', "/", full_path)
+                if plugin.is_file_excluded(fname) == False:
+                    if os.path.isdir(full_path):
+                        if plugin.non_recursively == False:
+                            exit_code += run_recursively(plugin, full_path)
+                    else:
+                        parser = plugin.get_plugin_loader().get_parser(full_path)
+                        if parser == None:
+                            logging.info("Skipping: " + norm_path)
+                        else:
+                            logging.info("Processing: " + norm_path)
+                            ts = time.time()
+                            f = open(full_path, 'r');
+                            text = f.read();
+                            f.close()
+                            checksum = binascii.crc32(text) & 0xffffffff # to match python 3
+    
+                            (data, is_updated) = plugin.get_plugin_loader().get_database_loader().create_file_data(full_path, checksum, text)
+                            procerrors = parser.process(plugin, data, is_updated)
+                            if plugin.is_proctime_enabled == True:
+                                data.set_data('general', 'proctime', time.time() - ts)
+                            if plugin.is_procerrors_enabled == True and procerrors != None and procerrors != 0:
+                                data.set_data('general', 'procerrors', procerrors)
+                            plugin.get_plugin_loader().get_database_loader().save_file_data(data)
+                            logging.debug("-" * 60)
+                            exit_code += procerrors
+                else:
+                    logging.info("Excluding: " + norm_path)
+                    logging.debug("-" * 60)
+            return exit_code
+        
+        total_errors = run_recursively(plugin, directory)
+        total_errors = total_errors # used, warnings are per file if not zero
+        return 0 # ignore errors, collection is successful anyway
+    
+
+
+    

+ 18 - 0
releases/metrixplusplus-1.1.70/core/export/__init__.py

@@ -0,0 +1,18 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#

+ 38 - 0
releases/metrixplusplus-1.1.70/core/export/convert.py

@@ -0,0 +1,38 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import core.export.utils.py2xml
+import core.export.utils.py2txt
+
+def to_xml(data, root_name = None):
+    serializer = core.export.utils.py2xml.Py2XML()
+    return serializer.parse(data, objName=root_name)
+
+def to_python(data, root_name = None):
+    prefix = ""
+    postfix = ""
+    if root_name != None:
+        prefix = "{'" + root_name + ": " 
+        postfix = "}"
+    return prefix + data.__repr__() + postfix
+
+def to_txt(data, root_name = None):
+    serializer = core.export.utils.py2txt.Py2TXT()
+    return serializer.parse(data, objName=root_name, indent = -1)

+ 40 - 0
releases/metrixplusplus-1.1.70/core/export/cout.py

@@ -0,0 +1,40 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+SEVERITY_INFO    = 0x01
+SEVERITY_WARNING = 0x02
+SEVERITY_ERROR   = 0x03
+
+def cout(path, cursor, level, message, details):
+    notification = path + ":" + str(cursor) + ": "
+    if level == SEVERITY_INFO:
+        notification += "info: "
+    elif level == SEVERITY_WARNING:
+        notification += "warning: "
+    elif level == SEVERITY_ERROR:
+        notification += "error: "
+    else:
+        assert(len("Invalid message severity level specified") == 0)
+    notification += message + "\n"
+
+    DETAILS_OFFSET = 15
+    for each in details:
+        notification += "\t" + str(each[0]) + (" " * (DETAILS_OFFSET - len(each[0]))) + ": " + str(each[1]) + "\n"
+        
+    print notification

+ 18 - 0
releases/metrixplusplus-1.1.70/core/export/utils/__init__.py

@@ -0,0 +1,18 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#

+ 138 - 0
releases/metrixplusplus-1.1.70/core/export/utils/py2txt.py

@@ -0,0 +1,138 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Copied from http://code.activestate.com/recipes/577268-python-data-structure-to-TXT-serialization/ and modified
+
+'''
+Py2TXT - Python to TXT serialization
+
+This code transforms a Python data structures into an TXT document
+
+Usage:
+    serializer = Py2TXT()
+    txt_string = serializer.parse( python_object )
+    print python_object
+    print txt_string
+'''
+
+INDENT_SPACE_SYMBOL = ".   " 
+
+class Py2TXT():
+
+    def __init__( self ):
+
+        self.data = "" # where we store the processed TXT string
+
+    def parse( self, pythonObj, objName=None, indent = 0 ):
+        '''
+        processes Python data structure into TXT string
+        needs objName if pythonObj is a List
+        '''
+        if pythonObj == None:
+            return "\n" + (INDENT_SPACE_SYMBOL * indent) + ""
+
+        if isinstance( pythonObj, dict ):
+            self.data = self._PyDict2TXT( pythonObj, objName, indent = indent + 1 )
+            
+        elif isinstance( pythonObj, list ):
+            # we need name for List object
+            self.data = self._PyList2TXT( pythonObj, objName, indent = indent + 1 )
+            
+        else:
+            self.data = "\n" + (INDENT_SPACE_SYMBOL * indent) + "%(n)s: %(o)s" % { 'n':objName, 'o':str( pythonObj ) }
+            
+        self.data = (INDENT_SPACE_SYMBOL * (indent + 1)) + "-" * 80 + self.data + "\n" + (INDENT_SPACE_SYMBOL * (indent + 1)) + "=" * 80 
+        return self.data
+
+    def _PyDict2TXT( self, pyDictObj, objName=None, indent = 0 ):
+        '''
+        process Python Dict objects
+        They can store TXT attributes and/or children
+        '''
+        tagStr = ""     # TXT string for this level
+        attributes = {} # attribute key/value pairs
+        attrStr = ""    # attribute string of this level
+        childStr = ""   # TXT string of this level's children
+
+        for k, v in pyDictObj.items():
+
+            if isinstance( v, dict ):
+                # child tags, with attributes
+                childStr += self._PyDict2TXT( v, k, indent = indent + 1 )
+
+            elif isinstance( v, list ):
+                # child tags, list of children
+                childStr += self._PyList2TXT( v, k, indent = indent + 1 )
+
+            else:
+                # tag could have many attributes, let's save until later
+                attributes.update( { k:v } )
+
+        if objName == None:
+            return childStr
+
+        # create TXT string for attributes
+        attrStr += ""
+        for k, v in attributes.items():
+            attrStr += "\n" + (INDENT_SPACE_SYMBOL * (indent + 1)) + "%s=\"%s\"" % ( k, v )
+
+        # let's assemble our tag string
+        if childStr == "":
+            tagStr += "\n" + (INDENT_SPACE_SYMBOL * indent) + "%(n)s: %(a)s" % { 'n':objName, 'a':attrStr }
+        else:
+            tagStr += "\n" + (INDENT_SPACE_SYMBOL * indent) + "%(n)s: %(a)s %(c)s" % { 'n':objName, 'a':attrStr, 'c':childStr }
+
+        return tagStr
+
+    def _PyList2TXT( self, pyListObj, objName=None, indent = 0 ):
+        '''
+        process Python List objects
+        They have no attributes, just children
+        Lists only hold Dicts or Strings
+        '''
+        tagStr = ""    # TXT string for this level
+        childStr = ""  # TXT string of children
+
+        for childObj in pyListObj:
+            
+            if isinstance( childObj, dict ):
+                # here's some Magic
+                # we're assuming that List parent has a plural name of child:
+                # eg, persons > person, so cut off last char
+                # name-wise, only really works for one level, however
+                # in practice, this is probably ok
+                childStr += "\n" + (INDENT_SPACE_SYMBOL * indent) + self._PyDict2TXT( childObj, objName[:-1], indent = indent + 1 )
+            elif isinstance( childObj, list ):
+                # here's some Magic
+                # we're assuming that List parent has a plural name of child:
+                # eg, persons > person, so cut off last char
+                # name-wise, only really works for one level, however
+                # in practice, this is probably ok
+                childStr += self._PyList2TXT( childObj, objName[:-1], indent = indent + 1 )
+            else:
+                childStr += "\n" + (INDENT_SPACE_SYMBOL * (indent + 1))
+                for string in childObj:
+                    childStr += string;
+
+        if objName == None:
+            return childStr
+
+        tagStr += "\n" + (INDENT_SPACE_SYMBOL * indent) + "%(n)s:%(c)s" % { 'n':objName, 'c':childStr }
+
+        return tagStr

+ 141 - 0
releases/metrixplusplus-1.1.70/core/export/utils/py2xml.py

@@ -0,0 +1,141 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+# Copied from http://code.activestate.com/recipes/577268-python-data-structure-to-xml-serialization/
+# - indent feature and better formatting added
+# - fixed handling of lists in lists
+# - fixed root object name for dictionaries
+
+INDENT_SPACE_SYMBOL = "    " 
+
+'''
+Py2XML - Python to XML serialization
+
+This code transforms a Python data structures into an XML document
+
+Usage:
+    serializer = Py2XML()
+    xml_string = serializer.parse( python_object )
+    print python_object
+    print xml_string
+'''
+
+class Py2XML():
+
+    def __init__( self ):
+
+        self.data = "" # where we store the processed XML string
+
+    def parse( self, pythonObj, objName=None, indent = 0 ):
+        '''
+        processes Python data structure into XML string
+        needs objName if pythonObj is a List
+        '''
+        if pythonObj == None:
+            return "\n" + (INDENT_SPACE_SYMBOL * indent) + ""
+
+        if isinstance( pythonObj, dict ):
+            self.data = self._PyDict2XML( pythonObj, objName, indent=indent+1 )
+            
+        elif isinstance( pythonObj, list ):
+            # we need name for List object
+            self.data = self._PyList2XML( pythonObj, objName, indent=indent+1 )
+            
+        else:
+            self.data = "\n" + (INDENT_SPACE_SYMBOL * indent) + "<%(n)s>%(o)s</%(n)s>" % { 'n':objName, 'o':str( pythonObj ) }
+            
+        return self.data
+
+    def _PyDict2XML( self, pyDictObj, objName=None, indent = 0 ):
+        '''
+        process Python Dict objects
+        They can store XML attributes and/or children
+        '''
+        tagStr = ""     # XML string for this level
+        attributes = {} # attribute key/value pairs
+        attrStr = ""    # attribute string of this level
+        childStr = ""   # XML string of this level's children
+
+        for k, v in pyDictObj.items():
+
+            if isinstance( v, dict ):
+                # child tags, with attributes
+                childStr += self._PyDict2XML( v, k, indent=indent+1 )
+
+            elif isinstance( v, list ):
+                # child tags, list of children
+                childStr += self._PyList2XML( v, k, indent=indent+1 )
+
+            else:
+                # tag could have many attributes, let's save until later
+                attributes.update( { k:v } )
+
+        if objName == None:
+            return childStr
+
+        # create XML string for attributes
+        for k, v in attributes.items():
+            attrStr += " %s=\"%s\"" % ( k, v )
+
+        # let's assemble our tag string
+        if childStr == "":
+            tagStr += "\n" + (INDENT_SPACE_SYMBOL * indent) + "<%(n)s%(a)s />" % { 'n':objName, 'a':attrStr }
+        else:
+            tagStr += ("\n" + (INDENT_SPACE_SYMBOL * indent) + "<%(n)s%(a)s>%(c)s" + "\n" + (INDENT_SPACE_SYMBOL * indent) + "</%(n)s>") % { 'n':objName, 'a':attrStr, 'c':childStr }
+
+        return tagStr
+
+    def _PyList2XML( self, pyListObj, objName=None, indent = 0 ):
+        '''
+        process Python List objects
+        They have no attributes, just children
+        Lists only hold Dicts or Strings
+        '''
+        tagStr = ""    # XML string for this level
+        childStr = ""  # XML string of children
+
+        for childObj in pyListObj:
+            
+            if isinstance( childObj, dict ):
+                # here's some Magic
+                # we're assuming that List parent has a plural name of child:
+                # eg, persons > person, so cut off last char
+                # name-wise, only really works for one level, however
+                # in practice, this is probably ok
+                childStr += self._PyDict2XML( childObj, objName[:-1], indent=indent+1 )
+            elif isinstance( childObj, list ):
+                # here's some Magic
+                # we're assuming that List parent has a plural name of child:
+                # eg, persons > person, so cut off last char
+                # name-wise, only really works for one level, however
+                # in practice, this is probably ok
+                childStr += self._PyList2XML( childObj, objName[:-1], indent=indent+1 )
+                pass
+            else:
+                childStr += "\n" + (INDENT_SPACE_SYMBOL * (indent + 1)) + "<" + objName[:-1] + ">"
+                for string in childObj:
+                    childStr += string;
+                childStr += "</" + objName[:-1] + ">"
+                
+        if objName == None:
+            return childStr
+
+        tagStr += ("\n" + (INDENT_SPACE_SYMBOL * indent) + "<%(n)s>%(c)s" + "\n" + (INDENT_SPACE_SYMBOL * indent) + "</%(n)s>") % { 'n':objName, 'c':childStr }
+
+        return tagStr

+ 26 - 0
releases/metrixplusplus-1.1.70/core/ext-priority/core.db.post.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ is free software: you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation, version 3 of the License.
+;    
+;    Metrix++ is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;    GNU General Public License for more details.
+;    
+;    You should have received a copy of the GNU General Public License
+;    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.0
+package: core.db
+module:  post
+class:   Plugin
+depends: None
+enabled: True

+ 26 - 0
releases/metrixplusplus-1.1.70/core/ext-priority/core.dir.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ is free software: you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation, version 3 of the License.
+;    
+;    Metrix++ is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;    GNU General Public License for more details.
+;    
+;    You should have received a copy of the GNU General Public License
+;    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.0
+package: core
+module:  dir
+class:   Plugin
+depends: None
+enabled: True

+ 26 - 0
releases/metrixplusplus-1.1.70/core/ext-priority/core.log.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ is free software: you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation, version 3 of the License.
+;    
+;    Metrix++ is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;    GNU General Public License for more details.
+;    
+;    You should have received a copy of the GNU General Public License
+;    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.0
+package: core
+module:  log
+class:   Plugin
+depends: None
+enabled: True

+ 133 - 0
releases/metrixplusplus-1.1.70/core/loader.py

@@ -0,0 +1,133 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import core.api
+import core.db.loader
+
+import os
+import fnmatch
+
+class Loader(object):
+
+    def __init__(self):
+        self.plugins = []
+        self.parsers = []
+        self.hash    = {}
+        self.db = core.db.loader.Loader()
+        
+    def get_database_loader(self):
+        return self.db
+
+    def get_plugin(self, name):
+        return self.hash[name]['instance']
+    
+    def iterate_plugins(self, is_reversed = False):
+        if is_reversed == False:
+            for item in self.plugins:
+                yield item['instance']
+        else:
+            for item in reversed(self.plugins):
+                yield item['instance']
+            
+    def register_parser(self, fnmatch_exp_list, parser):
+        self.parsers.append((fnmatch_exp_list, parser))
+
+    def get_parser(self, file_path):
+        for parser in self.parsers:
+            for fnmatch_exp in parser[0]:
+                if fnmatch.fnmatch(file_path, fnmatch_exp):
+                    return parser[1]
+        return None
+
+    def load(self, directory, optparser, args):
+        import sys
+        sys.path.append(directory)
+        
+        def load_recursively(manager, directory):
+            import ConfigParser
+            import re
+        
+            pattern = re.compile(r'.*[.]ini$', flags=re.IGNORECASE)
+        
+            dirList = os.listdir(directory)
+            for fname in dirList:
+                fname = os.path.join(directory, fname)
+                if os.path.isdir(fname):
+                    load_recursively(manager, fname)
+                elif re.match(pattern, fname):
+                    config = ConfigParser.ConfigParser()
+                    config.read(fname)
+                    item = {'package': config.get('Plugin', 'package'),
+                            'module': config.get('Plugin', 'module'),
+                            'class': config.get('Plugin', 'class'),
+                            'version': config.get('Plugin', 'version'),
+                            'depends': config.get('Plugin', 'depends'),
+                            'enabled': config.getboolean('Plugin', 'enabled')}
+                    if item['enabled']:
+                        manager.plugins.append(item)
+                        manager.hash[item['package'] + '.' + item['module']] = item
+
+        load_recursively(self, os.path.join(os.path.dirname(os.path.abspath(__file__)), 'ext-priority'))
+        load_recursively(self, directory)
+        # TODO check dependencies
+        for item in self.plugins:
+            plugin = __import__(item['package'], globals(), locals(), [item['module']], -1)
+            module_attr = plugin.__getattribute__(item['module'])
+            class_attr = module_attr.__getattribute__(item['class'])
+            item['instance'] = class_attr.__new__(class_attr)
+            item['instance'].__init__()
+            item['instance'].set_name(item['package'] + "." + item['module'])
+            item['instance'].set_version(item['version'])
+            item['instance'].set_plugin_loader(self)
+
+        for item in self.iterate_plugins():
+            if (isinstance(item, core.api.IConfigurable)):
+                item.declare_configuration(optparser)
+
+        (options, args) = optparser.parse_args(args)
+        for item in self.iterate_plugins():
+            if (isinstance(item, core.api.IConfigurable)):
+                item.configure(options)
+
+        for item in self.iterate_plugins():
+            item.initialize()
+            
+        return args
+
+    def unload(self):
+        for item in self.iterate_plugins(is_reversed = True):
+            item.terminate()
+
+    def run(self, args):
+        exit_code = 0
+        for item in self.iterate_plugins():
+            if (isinstance(item, core.api.IRunable)):
+                exit_code += item.run(args)
+        return exit_code
+
+    def __repr__(self):
+        result = object.__repr__(self) + ' with loaded:'
+        for item in self.iterate_plugins():
+            result += '\n\t' + item.__repr__()
+            if isinstance(item, core.api.Parent):
+                result += ' with subscribed:'
+                for child in item.iterate_children():
+                    result += '\n\t\t' + child.__repr__()
+        return result
+

+ 56 - 0
releases/metrixplusplus-1.1.70/core/log.py

@@ -0,0 +1,56 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import core.api
+import logging
+import os
+
+class Plugin(core.api.Plugin, core.api.IConfigurable):
+    
+    def declare_configuration(self, parser, default_value='INFO'):
+        allowed_values = ['DEBUG','INFO','WARNING','ERROR']
+        default_value_cur = default_value
+        if os.environ.has_key('METRIXPLUSPLUS_LOG_LEVEL') and os.environ['METRIXPLUSPLUS_LOG_LEVEL'] in allowed_values:
+            default_value_cur = os.environ['METRIXPLUSPLUS_LOG_LEVEL']
+        parser.add_option("--general.log-level", default=default_value_cur, choices=allowed_values,
+                         help="Defines log level. Possible values are 'DEBUG','INFO','WARNING' or 'ERROR'. "
+                         "Default value is inherited from environment variable 'METRIXPLUSPLUS_LOG_LEVEL' if set. "
+                         "[default: " + default_value + "]")
+    
+    def configure(self, options):
+        if options.__dict__['general.log_level'] == 'ERROR':
+            log_level = logging.ERROR
+        elif options.__dict__['general.log_level'] == 'WARNING':
+            log_level = logging.WARNING
+        elif options.__dict__['general.log_level'] == 'INFO':
+            log_level = logging.INFO
+        elif options.__dict__['general.log_level'] == 'DEBUG':
+            log_level = logging.DEBUG
+        else:
+            raise AssertionError("Unhandled choice of log level")
+        
+        self.level = log_level
+        logging.getLogger().setLevel(self.level)
+        os.environ['METRIXPLUSPLUS_LOG_LEVEL'] = options.__dict__['general.log_level']
+        logging.warn("Logging enabled with " + options.__dict__['general.log_level'] + " level")
+
+
+
+def set_default_format():
+    logging.basicConfig(format="[LOG]: %(levelname)s:\t%(message)s", level=logging.WARN)

+ 130 - 0
releases/metrixplusplus-1.1.70/core/warn.py

@@ -0,0 +1,130 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import re
+
+import core.api
+
+class Plugin(core.api.Plugin, core.api.IConfigurable):
+    
+    MODE_NEW     = 0x01
+    MODE_TREND   = 0x03
+    MODE_TOUCHED = 0x07
+    MODE_ALL     = 0x15
+    
+    
+    def declare_configuration(self, parser):
+        self.parser = parser
+        parser.add_option("--general.warn", default='all', choices=['new', 'trend', 'touched', 'all'],
+                         help="Defines the warnings mode. "
+                         "'new' - warnings for new regions only, "
+                         "'trend' - warnings for new regions and for bad trend of modified regions, "
+                         "'touched' - warnings for new regions and modified regions, "
+                         "'all' - all warnings active "
+                         "[default: %default]")
+
+        parser.add_option("--general.min-limit", action="multiopt",
+                          help="A threshold per 'namespace:field' metric in order to select regions, "
+                          "which have got metric value less than the specified limit. "
+                          "This option can be specified multiple times, if it is necessary to apply several limits. "
+                          "Should be in the format: <namespace>:<field>:<limit-value>, for example: "
+                          "'std.code.complexity:cyclomatic:7'.") # TODO think about better example
+        parser.add_option("--general.max-limit", action="multiopt",
+                          help="A threshold per 'namespace:field' metric in order to select regions, "
+                          "which have got metric value more than the specified limit. "
+                          "This option can be specified multiple times, if it is necessary to apply several limits. "
+                          "Should be in the format: <namespace>:<field>:<limit-value>, for example: "
+                          "'std.code.complexity:cyclomatic:7'.")
+        
+    def configure(self, options):
+        if options.__dict__['general.warn'] == 'new':
+            self.mode = self.MODE_NEW
+        elif options.__dict__['general.warn'] == 'trend':
+            self.mode = self.MODE_TREND
+        elif options.__dict__['general.warn'] == 'touched':
+            self.mode = self.MODE_TOUCHED
+        elif options.__dict__['general.warn'] == 'all':
+            self.mode = self.MODE_ALL
+            
+        if self.mode != self.MODE_ALL and options.__dict__['general.db_file_prev'] == None:
+            self.parser.error("The mode '" + options.__dict__['general.warn'] + "' for 'general.warn' option requires 'general.db-file-prev' option set")
+
+        class Limit(object):
+            def __init__(self, limit_type, limit, namespace, field, db_filter):
+                self.type = limit_type
+                self.limit = limit
+                self.namespace = namespace
+                self.field = field
+                self.filter = db_filter
+                
+            def __repr__(self):
+                return "namespace '" + self.namespace + "', filter '" + str(self.filter) + "'"
+        
+        self.limits = []
+        pattern = re.compile(r'''([^:]+)[:]([^:]+)[:]([-+]?[0-9]+(?:[.][0-9]+)?)''')
+        if options.__dict__['general.max_limit'] != None:
+            for each in options.__dict__['general.max_limit']:
+                match = re.match(pattern, each)
+                if match == None:
+                    self.parser.error("Invalid format of the 'general.max-limit' option: " + each)
+                limit = Limit("max", float(match.group(3)), match.group(1), match.group(2), (match.group(2), '>', float(match.group(3))))
+                self.limits.append(limit)
+        if options.__dict__['general.min_limit'] != None:
+            for each in options.__dict__['general.min_limit']:  
+                match = re.match(pattern, each)
+                if match == None:
+                    self.parser.error("Invalid format of the 'general.min-limit' option: " + each)
+                limit = Limit("min", float(match.group(3)), match.group(1), match.group(2), (match.group(2), '<', float(match.group(3))))
+                self.limits.append(limit)
+                
+    def verify_namespaces(self, valid_namespaces):
+        valid = []
+        for each in valid_namespaces:
+            valid.append(each)
+        for each in self.limits:
+            if each.namespace not in valid:
+                self.parser.error("Invalid limit option (namespace does not exist): " + each.namespace)
+
+    def verify_fields(self, namespace, valid_fields):
+        valid = []
+        for each in valid_fields:
+            valid.append(each)
+        for each in self.limits:
+            if each.namespace == namespace:
+                if each.field not in valid:
+                    self.parser.error("Invalid limit option (field does not exist): " + each.namespace + ":" + each.field)
+                    
+    def iterate_limits(self):
+        for each in self.limits:
+            yield each   
+
+    def is_mode_matched(self, limit, value, diff, is_modified):
+        if is_modified == None:
+            return True
+        if self.mode == self.MODE_ALL:
+            return True 
+        if self.mode == self.MODE_TOUCHED and is_modified == True:
+            return True 
+        if self.mode == self.MODE_TREND and is_modified == True:
+            if limit < value and diff > 0:
+                return True
+            if limit > value and diff < 0:
+                return True
+        return False
+        

+ 35 - 0
releases/metrixplusplus-1.1.70/doc/index-online.html

@@ -0,0 +1,35 @@
+<!--
+
+    Metrix++, Copyright 2009-2013, Metrix++ Project
+    Link: http://metrixplusplus.sourceforge.net
+    
+    This file is part of Metrix++ Tool.
+    
+    Metrix++ is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, version 3 of the License.
+    
+    Metrix++ is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+    GNU General Public License for more details.
+    
+    You should have received a copy of the GNU General Public License
+    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="cache-control" content="max-age=0" />
+<meta http-equiv="cache-control" content="no-cache" />
+<meta http-equiv="expires" content="0" />
+<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
+<meta http-equiv="pragma" content="no-cache" />
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-5" />
+<meta http-equiv="REFRESH" content="0;url=http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/doc/project.html">
+<title>Metrix++ Index Page</title>
+</head>
+<body>
+</body>
+</html>

+ 35 - 0
releases/metrixplusplus-1.1.70/doc/index.html

@@ -0,0 +1,35 @@
+<!--
+
+    Metrix++, Copyright 2009-2013, Metrix++ Project
+    Link: http://metrixplusplus.sourceforge.net
+    
+    This file is part of Metrix++ Tool.
+    
+    Metrix++ is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, version 3 of the License.
+    
+    Metrix++ is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+    GNU General Public License for more details.
+    
+    You should have received a copy of the GNU General Public License
+    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="cache-control" content="max-age=0" />
+<meta http-equiv="cache-control" content="no-cache" />
+<meta http-equiv="expires" content="0" />
+<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
+<meta http-equiv="pragma" content="no-cache" />
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-5" />
+<meta http-equiv="REFRESH" content="0;url=./project.html">
+<title>Metrix++ Index Page</title>
+</head>
+<body>
+</body>
+</html>

+ 674 - 0
releases/metrixplusplus-1.1.70/doc/license.txt

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ 69 - 0
releases/metrixplusplus-1.1.70/doc/limitations.txt

@@ -0,0 +1,69 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+===============================================================================
+
+*** Known Limitations ***
+
+1) C/C++, C# and Java parsers do not recognise definition of functions or overloaded operators
+   in case of embeded comments after identifier name and before the list of arguments.
+
+     This function is not detected by Metrix++: | This function is detected:
+     -------------------------------------------|-------------------------------------------
+         int getMax /* undesarable comment */   |     int getMax(int* array, int length)
+             (int* array, int length)           |         /* here is fine */
+         {                                      |     {
+             /* ... */                          |         /* ... */
+         }                                      |     }
+
+2) C/C++ parser does not recognise comments within preprocessor statements.
+   These comments are considered to be parts of a define.
+
+     This comment is not recognized by Metrix++:| This comment is recognised.
+     -------------------------------------------|-------------------------------------------
+         #define GET_MAX(a, b)    \             |     /*
+             /*                      \          |      * This macros returns
+              * This macros returns  \          |      * maximum from a and b
+              * maximum from a and b \          |      */
+              */                     \          |     #define GET_MAX(a, b) \
+             ((a > b) ? a : b)                  |         ((a > b) ? a : b)
+
+3) C# parser does not recognise getters/setters for properties, if there is a comment before a block.
+   These comments are considered to be parts of a define.
+
+     This function is not detected by Metrix++: | This function is detected:
+     -------------------------------------------|-------------------------------------------
+         get /* undesarable comment */          |     get
+         {                                      |     { /* here comment is fine */
+             /* ... */                          |         /* ... */
+         }                                      |     }
+
+4) Java parser does not recognise anonymous inner classes.
+
+5) C/C++, C# and Java parsers do not recognise definition of classes/structs/namespaces/interface)
+   in case of embeded comments after keyword and identifier name.
+
+     This class is not detected by Metrix++:    | This class is detected:
+     -------------------------------------------|-------------------------------------------
+         class /* comment */ MyClass            |     class MyClass /* here is fine */
+         {                                      |     {
+             /* ... */                          |         /* ... */
+         }                                      |     }
+
+6) Java parser does not support parsing of identifiers which have got unicode symbols in name.

BIN
releases/metrixplusplus-1.1.70/doc/logo.png


BIN
releases/metrixplusplus-1.1.70/doc/logo_project.png


BIN
releases/metrixplusplus-1.1.70/doc/logo_small.gif


BIN
releases/metrixplusplus-1.1.70/doc/logo_small.png


+ 546 - 0
releases/metrixplusplus-1.1.70/doc/project.html

@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="iso-8859-5"?>
+<!--
+
+Metrix++, Copyright 2009-2013, Metrix++ Project
+Link: http://metrixplusplus.sourceforge.net
+
+This file is part of Metrix++ Tool.
+
+Metrix++ is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+Metrix++ is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Metrix++. If not, see <http://www.gnu.org/licenses/>.
+
+-->
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <meta http-equiv="cache-control" content="max-age=0" />
+  <meta http-equiv="cache-control" content="no-cache" />
+  <meta http-equiv="expires" content="0" />
+  <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
+  <meta http-equiv="pragma" content="no-cache" />
+  <meta http-equiv="content-type" content="text/html; charset=iso-8859-5" />
+  <title>Metrix++ Project</title>
+  <link href="style.css" rel="stylesheet" type="text/css" />
+</head>
+
+<body>
+
+<table border="0" align="center" cellpadding="0" cellspacing="0" width="800">
+  <tbody>
+    <tr>
+      <td></td>
+      <td valign="middle"></td>
+    </tr>
+    <tr>
+      <td><img src="logo_project.png" alt="Software Index Project Page" /></td>
+      <td valign="top">
+        <div align="right">
+        <p align="center"><a
+        href="http://sourceforge.net/projects/metrixplusplus"
+        class="lowImportance"><img
+        src="http://sflogo.sourceforge.net/sflogo.php?group_id=275605&amp;type=13"
+        alt="Get Software Index at SourceForge.net. Fast, secure and Free Open Source software downloads"
+        border="0" /></a></p>
+
+        <p align="center"><script type="text/javascript"
+        src="http://www.ohloh.net/p/485947/widgets/project_users_logo.js">
+        </script>
+        </p>
+        </div>
+      </td>
+    </tr>
+    <tr>
+      <td colspan="2"><hr />
+      </td>
+    </tr>
+    <tr>
+      <td colspan="2" class="highImportance"
+      style="text-align:center;margin-left:auto;margin-right:0;"><a
+        href="#Overview">Overview</a> | <a href="#Download">Download</a> | <a
+        href="#Documentation">Documentation</a> | <a href="#Support">Bur
+        report</a> | <a href="#Support">Feature request</a> | <a
+        href="#Createplugin">Create plugin</a></td>
+    </tr>
+    <tr>
+      <td colspan="2"><hr />
+      </td>
+    </tr>
+    <tr>
+      <td colspan="2" valign="top">
+        <table width="100%" border="0" cellspacing="0" cellpadding="10">
+          <tbody>
+            <tr>
+              <td valign="top"><h3 id="Overview">Overview</h3>
+
+                <p><span class="normalImportance">Metrix++</span> is the
+                platform to collect and analyse code metrics.</p>
+                <ul>
+                  <li>It has got plugin based architecture, so it is easy to
+                    add support for new languages and/or define new metrics
+                    and/or create new pre- and post-processing tools.</li>
+                  <li>Every metric has got 'turn-on' and other configuration
+                    options.</li>
+                  <li>There is no predefined thresholds for metrics or rules.
+                    You can choose and configure any limit you want.</li>
+                  <li>It scales well and support big code bases. For example
+                    initial parsing of about 10000 files takes 2-3 minutes on
+                    average PC, and ONLY 10-20 seconds for iterative re-run.
+                    Reporting, analysis and other postprocessings of results
+                    take few seconds.</li>
+                  <li>It can compare results for 2 code snapshots (collections)
+                    and differentiate added regions (classes, functions, etc.),
+                    modified regions and unchanged regions.</li>
+                  <li>As a result, easy deployment is guaranteed into legacy
+                    software, helping you to deal with legacy code effiently -
+                    either enforce 'make it [legacy code] not worse' or
+                    're-factor if it is touched' policies.</li>
+                </ul>
+
+                <h4>Standard Plugins</h4>
+
+                <p>The distributive includes a set of standard plugins:</p>
+                <ul>
+                  <li><h4>Code parsers</h4>
+                    <ul>
+                      <li><span class="normalImportance">C/C++</span> parser
+                        recognises definition of namespaces, definition of
+                        classes/structs (including enclosed in functions),
+                        templates and definition of functions/operators</li>
+                      <li><span class="normalImportance">C#</span> parser
+                        recognises definition of namespaces, definition of
+                        classes/structs (including enclosed in functions),
+                        interfaces, generics, definition of
+                      functions/operators</li>
+                      <li><span class="normalImportance">Java</span> parser
+                        recognises definition of classes (including local in
+                        functions), interfaces, generics and functions</li>
+                    </ul>
+                  </li>
+                  <li><h4>Metrics</h4>
+                    <ul>
+                      <li><span class="normalImportance">cyclomatic
+                        complexity</span> (by McCabe) per function <span
+                        class="lowImportance">[supports C/C++, C#, Java
+                        languages]</span></li>
+                      <li><span class="normalImportance">processing
+                        errors</span> per file <span
+                        class="lowImportance">[supports any file
+                      type]</span></li>
+                      <li><span class="normalImportance">processing time</span>
+                        per file <span class="lowImportance">[supports any file
+                        type]</span></li>
+                    </ul>
+                  </li>
+                  <li><h4>Analysis tools</h4>
+                    <ul>
+                      <li><span class="normalImportance">export</span> -
+                        exporter to xml, python or plain text of detailed
+                        information per file and/or aggregated information per
+                        file or directory <span
+                        class="lowImportance">[aggregated data includes <span
+                        class="normalImportance">sum</span>, <span
+                        class="normalImportance">maximum</span>, <span
+                        class="normalImportance">minimum</span>, <span
+                        class="normalImportance">average</span> within a subset
+                        of selected files or directories]</span></li>
+                      <li><span class="normalImportance">limit</span> - a tool
+                        to report regions exceeding speified thresholds, which
+                        are configurable <span class="lowImportance">[output is
+                        plain text with metadata compatible with gcc compiler
+                        warning messages]</span></li>
+                      <li><span class="normalImportance">info</span> - a tool
+                        to show file metadata, such as properties, namespaces
+                        and fields recorded, files processed</li>
+                    </ul>
+                  </li>
+                </ul>
+
+                <h3 id="Download">Download &amp; Installation</h3>
+
+                <p>For the installation of the <span
+                class="normalImportance">Metrix++</span> <a
+                href="http://sourceforge.net/projects/metrixplusplus/files/">download
+                the archive</a> with <span class="highImportance">the latest
+                stable version</span> and unpack it to some folder.
+                Corresponding checkout from the version control system:</p>
+
+                <p><pre>&gt; svn checkout <a href="https://metrixplusplus.svn.sourceforge.net/svnroot/metrixplusplus/releases/">https://metrixplusplus.svn.sourceforge.net/svnroot/metrixplusplus/releases/</a>&lt;version&gt;</pre>
+                </p>
+
+                <p>Alternatively, there is an option to <a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/?view=tar">download
+                tarball file</a> with <span class="highImportance">the latest
+                development version</span> of the tool or checkout this version
+                of <a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/">source
+                code</a> from the version conrol system:</p>
+
+                <p><pre>&gt; svn checkout <a href="https://metrixplusplus.svn.sourceforge.net/svnroot/metrixplusplus/mainline/">https://metrixplusplus.svn.sourceforge.net/svnroot/metrixplusplus/mainline/</a></pre>
+                </p>
+
+                <h4>Prerequisites:</h4>
+
+                <p>Python Runtime Environment (version 2.7.* or later, version
+                3.* has not been tested)</p>
+
+                <h4>License:</h4>
+
+                <p>This program is free software; you can redistribute it
+                and/or modify it under the terms of the GNU General Public
+                License as published by the Free Software Foundation; version 3
+                of the License.</p>
+
+                <p>This program is distributed in the hope that it will be
+                useful, but WITHOUT ANY WARRANTY; without even the implied
+                warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+                PURPOSE. See the GNU General Public License for more
+                details.</p>
+
+                <p>You should have received a copy of the GNU General Public
+                License along with the <span
+                class="normalImportance">Metriix++</span>; if not, contact <a
+                href="mailto:avkonst@users.sourceforge.net">Project
+                Administrator</a> and write to the Free Software Foundation,
+                Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
+                USA.</p>
+
+                <h3 id="Documentation">Documentation</h3>
+
+                <p>Tools are self-descriptive and have got comprehensive
+                context help. Type in the command line: python metrixpp.py
+                &lt;tool-name&gt; --help</p>
+
+                <h4>Known Limitations</h4>
+
+                <p>Check 'doc/limitations.txt' file distributed with the tool
+                or browse for recent version <a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/doc/limitations.txt">online</a>.</p>
+
+                <h4>Basic Workflow</h4>
+
+                <p>Assuming that you opened a terminal and your current working
+                directory is in the root folder of the tool installed.</p>
+
+                <p><strong>I.</strong> Start with running a collector tool,
+                enabling a collection of cyclomatic complexity:</p>
+
+                <p><pre>&gt; python metixpp.py <span class="highImportance">collect</span> <span class="normalImportance">--std.code.complexity.on</span> -- /path/to/your/project ../../path/to/some/other/project</pre>
+                </p>
+
+                <p>It will generate a database file in working directory with
+                default name. In order to change the name or location, use
+                corresponding command line option. Depending on a size of code
+                base, collection may take seconds or minutes, but it is very
+                fast in general. In order to achive the highest performance (~
+                to calculation of crc32) for iterative re-scans, point out to
+                the dabase file collected for the previous code revision:</p>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">collect</span> --std.code.complexity.on <span class="highImportance normalImportance">--general.db-file-prev=metrixpp-prev.db</span> --
+ /path/to/your/project</pre>
+                </p>
+
+                <p>Paths are optional. If you do not specify a path, it will
+                scan files in the current working directory. Check other
+                available options for the tool by executing:</p>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">collect</span> <span class="normalImportance">--help</span></pre>
+                </p>
+
+                <p><strong>II.</strong> Secondly, export data using export
+                tool.</p>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">export</span> --general.format=<span class="normalImportance">xml</span> --
+<span class="lowImportance">&lt;export&gt;
+    &lt;data&gt;
+        &lt;info path="" id="1" /&gt;
+        &lt;file-data /&gt;
+        &lt;subfiles&gt;
+        &lt;/subfiles&gt;
+        &lt;subdirs&gt;
+            &lt;subdir&gt;path&lt;/subdir&gt;
+        &lt;/subdirs&gt;
+        &lt;aggregated-data&gt;
+            &lt;<span class="normalImportance">std.code.complexity</span>&gt;
+                &lt;cyclomatic max="2" total="14.0" avg="0.168674698795" min="0" /&gt;
+            &lt;/<span class="normalImportance">std.code.complexity</span>&gt;
+        &lt;/aggregated-data&gt;
+    &lt;/data&gt;
+&lt;/export&gt;</span>
+                </pre>
+                </p>
+
+                <p>By default, it exports all aggregated data for all files
+                scanned. In order to narrow the scope, specify a path to
+                directory or a file. In order to export only subset of metrics,
+                specify them as well. It is also possible to change output
+                format too.</p>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">export</span> --general.format=<span class="normalImportance">python</span> --general.namespaces=<span class="normalImportance">std.code.complexity</span> --
+ <span class="normalImportance">/path/to/your/project/subdir</span>
+<span class="lowImportance">    ...</span></pre>
+                </p>
+
+                <p>If you have got results collected for previous version,
+                point out to the file using the corresponding option and the
+                tool will add diff data.</p>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">export</span>  --general.format=<span class="normalImportance">xml</span> --general.namespaces=<span class="normalImportance">std.code.complexity</span> --general.db-file-prev=<span class="normalImportance">metrixpp-prev.db</span> --
+<span class="lowImportance">&lt;export&gt;
+    &lt;data&gt;
+        &lt;info path="" id="1" /&gt;
+        &lt;file-data /&gt;
+        &lt;subfiles&gt;
+        &lt;/subfiles&gt;
+        &lt;subdirs&gt;
+            &lt;subdir&gt;path&lt;/subdir&gt;
+        &lt;/subdirs&gt;
+        &lt;aggregated-data&gt;
+            &lt;<span class="normalImportance">std.code.complexity</span>&gt;
+                &lt;cyclomatic max="2" total="14.0" avg="0.168674698795" min="0"&gt;
+                    &lt;<span class="normalImportance">__diff__ max="0" total="0.0" avg="0.0" min="0"</span> /&gt;
+                &lt;/cyclomatic&gt;
+            &lt;/<span class="normalImportance">std.code.complexity</span>&gt;
+        &lt;/aggregated-data&gt;
+    &lt;/data&gt;
+&lt;/export&gt;</span>
+                </pre>
+                </p>
+
+                <p>Check other available options for the tool by executing:</p>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">export</span> <span class="normalImportance">--help</span></pre>
+                </p>
+
+                <p><strong>III.</strong> Finally, identify code regions which
+                exceed a specified limit, applying it to all scanned files:</p>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">limit</span> --general.max-limit=<span class="normalImportance">std.code.complexity</span><strong>:</strong><span class="normalImportance">cyclomatic</span><strong>:</strong><span class="normalImportance">7</span><strong> </strong>--
+<span class="lowImportance">/path/to/your/project/ui/notifications.cpp:72: warning: Metric '<span class="normalImportance">std.code.complexity</span>/<span class="normalImportance">cyclomatic</span>' for
+ region 'doFont' exceeds the limit.
+        Metric name    : <span class="normalImportance">std.code.complexity</span>/<span class="normalImportance">cyclomatic</span>
+        Region name    : doFont
+        Metric value   : 10
+        Modified       : None
+        Change trend   : None
+        Limit          : 7
+/path/to/your/project/ui/notifications.cpp:144: warning: Metric '<span class="normalImportance">std.code.complexity</span>/<span class="normalImportance">cyclomatic</span>' for
+ region 'doStyle' exceeds the limit.
+        Metric name    : <span class="normalImportance">std.code.complexity</span>/<span class="normalImportance">cyclomatic</span>
+        Region name    : doStyle
+        Metric value   : 9
+        Modified       : None
+        Change trend   : None
+        Limit          : 7</span></pre>
+                </p>
+
+                <p>You can limit the scope of analysis by defining paths to
+                directories or files. If you have got results collected for
+                previous version, point out to the file using the corresponding
+                option and the tool will inform about change trends:</p>
+
+                <p><pre>&gt; python <span class="highImportance">limit.py</span> --general.max-limit=std.code.complexity<strong>:</strong>cyclomatic<strong>:</strong>7
+ --general.db-file-prev=<span class="normalImportance">metrixpp-prev.db</span> --
+<span class="lowImportance normalImportance">/path/to/your/project/ui/notifications.cpp:72: warning: Metric 'std.code.complexity/cyclomatic' for
+ region 'doFont' exceeds the limit.
+        Metric name    : std.code.complexity/cyclomatic
+        Region name    : doFont
+        Metric value   : 10
+        Modified       : <span class="normalImportance">True</span>
+        Change trend   : <span class="normalImportance">0</span>
+        Limit          : 7
+/path/to/your/project/ui/notifications.cpp:144: warning: Metric 'std.code.complexity/cyclomatic' for
+ region 'doStyle' exceeds the limit.
+        Metric name    : std.code.complexity/cyclomatic
+        Region name    : doStyle
+        Metric value   : 9
+        Modified       : <span class="normalImportance">True</span>
+        Change trend   : <span class="normalImportance">+1</span>
+        Limit          : 7</span></pre>
+                </p>
+
+                <p>Another useful option for this tool helps you to <span
+                class="highImportance">deal with legacy code</span>. It is
+                normal that you have got enormous number of warnings for the
+                code designed in the past, which has not be profiled/targeted
+                to specific metric limits. By default the tool warns about all
+                code regions, ignoring their change status. You can reconfigure
+                it to one of the following:</p>
+                <ul>
+                  <li>warn only about <span class="normalImportance">new</span>
+                    code regions (functions, classes):</li>
+                </ul>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">limit</span> --general.max-limit=std.code.complexity<strong>:</strong>cyclomatic<strong>:</strong>7
+ --general.db-file-prev=metrixpp-prev.db <span class="normalImportance">--general.warn=<strong>new</strong></span> --</pre>
+                </p>
+                <ul>
+                  <li>warn about <span class="normalImportance">new</span> code
+                    regions and <span class="normalImportance">modifed regions
+                    <strong>regressing</strong> the metric</span> (enforces the
+                    rule 'leave not worse than it was before'):</li>
+                </ul>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">limit</span> --general.max-limit=std.code.complexity<strong>:</strong>cyclomatic<strong>:</strong>7
+ --general.db-file-prev=metrixpp-prev.db <span class="normalImportance">--general.warn=<strong>trend</strong></span> --
+<span class="lowImportance normalImportance">/path/to/your/project/ui/notifications.cpp:144: warning: Metric 'std.code.complexity/cyclomatic' for
+ region 'doStyle' exceeds the limit.
+        Metric name    : std.code.complexity/cyclomatic
+        Region name    : doStyle
+        Metric value   : 9
+        Modified       : <span class="normalImportance">True</span>
+        Change trend   : <span class="normalImportance"><strong>+1</strong></span>
+        Limit          : 7</span></pre>
+                </p>
+                <ul>
+                  <li>warn about <span class="normalImportance">new</span> and
+                    <span class="normalImportance">all modified regions</span>
+                    (motivates for refactoring of legacy code): </li>
+                </ul>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">limit</span> --general.max-limit=std.code.complexity<strong>:</strong>cyclomatic<strong>:</strong>7
+ --general.db-file-prev=metrixpp-prev.db <span class="normalImportance">--general.warn=<strong>touched</strong></span> --
+<span class="lowImportance normalImportance">/path/to/your/project/ui/notifications.cpp:72: warning: Metric 'std.code.complexity/cyclomatic' for
+ region 'doFont' exceeds the limit.
+        Metric name    : std.code.complexity/cyclomatic
+        Region name    : doFont
+        Metric value   : 10
+        Modified       : <span class="normalImportance"><strong>True</strong></span>
+        Change trend   : <span class="normalImportance">0</span>
+        Limit          : 7
+/path/to/your/project/ui/notifications.cpp:144: warning: Metric 'std.code.complexity/cyclomatic' for
+ region 'doStyle' exceeds the limit.
+        Metric name    : std.code.complexity/cyclomatic
+        Region name    : doStyle
+        Metric value   : 9
+        Modified       : <strong><span class="normalImportance">True</span></strong>
+        Change trend   : <span class="normalImportance">+1</span>
+        Limit          : 7</span></pre>
+                </p>
+
+                <p>Check other available options for the tool by executing:</p>
+
+                <p><pre>&gt; python metrixpp.py <span class="highImportance">limit</span> <span class="normalImportance">--help</span></pre>
+                </p>
+
+                <h3 id="Support">Bug Report &amp; Feature Request</h3>
+
+                <p>Any types of enquiries are welcomed by e-mail to <a
+                href="mailto:avkonst@users.sourceforge.net">project
+                administrator</a>.</p>
+
+                <h3 id="Createplugin">Create Plugin</h3>
+
+                <p>Please, consider to join the project and <span
+                class="normalImportance">contribute to the development</span>
+                of the engine or <span class="normalImportance">include you
+                plugins into the standard set</span> of plugins distributed
+                together with this tool. Contact <a
+                href="mailto:avkonst@users.sourceforge.net">project
+                administrator</a> by e-mail.</p>
+
+                <h4>New Metric</h4>
+
+                <p>Unfortunately, there is no rich documentation at this stage.
+                Briefly, any new plugin starts with creating 2 new files: 'ini'
+                and 'py'. You can copy from other avaialble (for example, <a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/ext/std/code/complexity.ini">ini</a>
+                and <a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/ext/std/code/complexity.py">py</a>
+                files for the standard complexity plugin) and impelement the
+                logic related to the new metric.</p>
+
+                <h4>New Analysis Tool</h4>
+
+                <p>Unfortunately, there is no rich documentation at this stage.
+                Briefly, database API (class Loader implemented in '<a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/core/db/loader.py">core.db.loader</a>')
+                is a starting point for any new post-analysis tool. There are 2
+                standard tools (<a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/tools/export.py">export</a>
+                and <a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/tools/limit.py">limit</a>)
+                available which use this API.</p>
+
+                <h4>New Language Support</h4>
+
+                <p>Unfortunately, there is no rich documentation at this stage.
+                Briefly:</p>
+                <ul>
+                  <li>a plugin is registered in the same way as a plugin for
+                    new metric</li>
+                  <li>it subscribes to directory reader plugin</li>
+                  <li>parses a file in a callback, called by directory
+                  reader</li>
+                  <li>parser needs to identify markers (like comments, strings,
+                    preprocessor) and regions (like functions, classes, etc.)
+                    and tell about this to file data object passed as an
+                    argument for the callback.</li>
+                </ul>
+
+                <p>Parser for C/C++ language can serve as <a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/ext/std/code/cpp.py">an
+                example</a>. There useful options and tools are avaialble for
+                trobuleshooting purposes during development:</p>
+                <ul>
+                  <li><span class="normalImportance">metrixpp.py debug</span>
+                    tool is helpful troubleshooting tool. In mode 'dumphtml' it
+                    generates html code showing code highlightings</li>
+                  <li><span
+                    class="normalImportance">--general.nest-regions</span> for
+                    export tool forces exporting of code structure in tree
+                    format. It can be helpful for analysis of parser's
+                  internals</li>
+                  <li><span
+                    class="normalImportance">--general.log-level</span>=GENERAL.LOG_LEVEL
+                    for any tool is helpful to trace execution </li>
+                </ul>
+
+                <p>Finally, if there are any questions or enquires, please,
+                feel free to contact <a
+                href="mailto:avkonst@users.sourceforge.net">project
+                administrator</a> by e-mail.</p>
+
+                <h4>Source Code Repository</h4>
+
+                <p>Source code is in the Subversion repository <a
+                href="http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/">(browse
+                online)</a> used by the project. Corresponding checkout command
+                is the following:</p>
+
+                <p><pre>&gt; svn checkout <a href="https://metrixplusplus.svn.sourceforge.net/svnroot/metrixplusplus/mainline/">https://metrixplusplus.svn.sourceforge.net/svnroot/metrixplusplus/mainline/</a></pre>
+                </p>
+
+                <h4></h4>
+              </td>
+            </tr>
+          </tbody>
+        </table>
+        <hr />
+      </td>
+    </tr>
+    <tr>
+      <td colspan="2">
+        <div align="center">
+        <p align="right">Copyright <strong>&copy; </strong>2009 - 2013<br />
+        <a href="mailto:avkonst@users.sourceforge.net"><span
+        class="normalImportance">Metrix++</span> Project</a></p>
+
+        <p align="right">License: <a
+        href="http://www.gnu.org/licenses/gpl.txt">GPL</a></p>
+        </div>
+      </td>
+    </tr>
+  </tbody>
+</table>
+</body>
+</html>

+ 130 - 0
releases/metrixplusplus-1.1.70/doc/style.css

@@ -0,0 +1,130 @@
+/*
+
+    Metrix++, Copyright 2009-2013, Metrix++ Project
+    Link: http://metrixplusplus.sourceforge.net
+    
+    This file is a part of Metrix++ Tool.
+    
+    Metrix++ is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, version 3 of the License.
+    
+    Metrix++ is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+    GNU General Public License for more details.
+    
+    You should have received a copy of the GNU General Public License
+    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+body{
+	font-family: Arial, sans-serif;
+	color: #333333;
+	line-height: 1.166;
+	margin: 0px;
+	padding: 10px;
+	font-size: 14px;
+}
+
+pre{
+    font-size: 100%;
+    padding-left: 50px;
+    font-family:Courier;
+}
+
+td{
+	background-position: left;
+	background-repeat: no-repeat;
+}
+
+a{
+	color: #AA0033;
+	text-decoration: none;
+}
+
+a:link{
+	color: #AA0033;
+	text-decoration: none;
+}
+
+a:visited{
+	color: #AA0033;
+	text-decoration: none;
+}
+
+a:hover{
+	color: #FF9933;
+	text-decoration: underline;
+}
+
+h1{
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 130%;
+ color: #116633;
+ margin: 0px;
+ padding: 0px;
+}
+
+h2{
+ font-family: Arial,sans-serif;
+ font-size: 120%;
+ color: #330099;
+ margin: 0px;
+ padding: 0px;
+}
+
+h3{
+ font-family: Arial, sans-serif;
+ font-size: 120%;
+ color: #AA0033;
+ margin: 0px;
+ padding: 0px;
+}
+
+h4{
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 100%;
+ color: #334d55;
+ margin: 0px;
+ padding: 0px;
+}
+
+h5{
+ margin: 0px;
+ padding: 0px;
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 100%;
+ color: #334d55;
+}
+
+ul{
+ list-style-type: disc;
+}
+
+ul ul{
+ list-style-type: circle;
+}
+
+ul ul ul{
+ list-style-type: disc;
+}
+
+label{
+	font-family: Arial, sans-serif;
+	font-size: 100%;
+	color: #116633;
+}
+
+.highImportance {
+	color: #FF0000
+}
+
+.normalImportance {
+	color: #009900
+}
+
+.lowImportance{
+    color: #999999;
+}

+ 0 - 0
releases/metrixplusplus-1.1.70/ext/std/__init__.py


+ 18 - 0
releases/metrixplusplus-1.1.70/ext/std/code/__init__.py

@@ -0,0 +1,18 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#

+ 26 - 0
releases/metrixplusplus-1.1.70/ext/std/code/complexity.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ is free software: you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation, version 3 of the License.
+;    
+;    Metrix++ is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;    GNU General Public License for more details.
+;    
+;    You should have received a copy of the GNU General Public License
+;    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.1
+package: std.code
+module:  complexity
+class:   Plugin
+depends: None
+enabled: True

+ 75 - 0
releases/metrixplusplus-1.1.70/ext/std/code/complexity.py

@@ -0,0 +1,75 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import core.api
+
+import re
+
+class Plugin(core.api.Plugin, core.api.Child, core.api.IConfigurable):
+    
+    def declare_configuration(self, parser):
+        parser.add_option("--std.code.complexity.on", action="store_true", default=False,
+                         help="Enables collection of complexity metrics: cyclomatic by McCabe [default: %default]")
+    
+    def configure(self, options):
+        self.is_active = options.__dict__['std.code.complexity.on']
+        
+    def initialize(self):
+        if self.is_active == True:
+            # trigger version property set
+            core.api.Plugin.initialize(self)
+            namespace = self.get_plugin_loader().get_database_loader().create_namespace(self.get_name(), support_regions = True)
+            namespace.add_field('cyclomatic', int)
+            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')
+
+    # cyclomatic complexity pattern
+    # - C/C++
+    pattern_cpp = re.compile(r'''([^0-9A-Za-z_]((if)|(case)|(for)|(while)|(catch))[^0-9A-Za-z_])|[&][&]|[|][|]|[?]''')
+    # - C#
+    #   supports Null-coalescing '??' and conditional '?:'
+    pattern_cs = re.compile(r'''([^0-9A-Za-z_]((if)|(case)|(for)|(foreach)|(while)|(catch))[^0-9A-Za-z_])|[&][&]|[|][|]|[?][?]?''')
+    # - Java
+    pattern_java = re.compile(r'''([^0-9A-Za-z_]((if)|(case)|(for)|(while)|(catch))[^0-9A-Za-z_])|[&][&]|[|][|]|[?]''')
+
+    def callback_cpp(self, parent, data, is_updated):
+        self.callback_common(parent, data, is_updated, self.pattern_cpp)
+
+    def callback_cs(self, parent, data, is_updated):
+        self.callback_common(parent, data, is_updated, self.pattern_cs)
+
+    def callback_java(self, parent, data, is_updated):
+        self.callback_common(parent, data, is_updated, self.pattern_java)
+
+    def callback_common(self, parent, data, is_updated, pattern):
+        is_updated = is_updated or self.is_updated
+        if is_updated == True:
+            text = data.get_content(exclude = data.get_marker_types().ALL_EXCEPT_CODE)
+            for region in data.iterate_regions(filter_group=data.get_region_types().FUNCTION):
+                # cyclomatic complexity
+                count = 0
+                start_pos = region.get_offset_begin()
+                for sub_id in region.iterate_subregion_ids():
+                    # exclude sub regions, like enclosed classes
+                    count += len(pattern.findall(text, start_pos, data.get_region(sub_id).get_offset_begin()))
+                    start_pos = data.get_region(sub_id).get_offset_end()
+                count += len(pattern.findall(text, start_pos, region.get_offset_end()))
+                region.set_data(self.get_name(), 'cyclomatic', count)
+

+ 26 - 0
releases/metrixplusplus-1.1.70/ext/std/code/cpp.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ is free software: you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation, version 3 of the License.
+;    
+;    Metrix++ is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;    GNU General Public License for more details.
+;    
+;    You should have received a copy of the GNU General Public License
+;    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.1
+package: std.code
+module:  cpp
+class:   Plugin
+depends: None
+enabled: True

+ 304 - 0
releases/metrixplusplus-1.1.70/ext/std/code/cpp.py

@@ -0,0 +1,304 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import re
+import binascii
+import logging
+
+import core.api
+
+class Plugin(core.api.Plugin, core.api.Parent, core.api.IParser, core.api.IConfigurable, core.api.ICode):
+    
+    def declare_configuration(self, parser):
+        parser.add_option("--std.code.cpp.files", default="*.c,*.h,*.cpp,*.hpp,*.cc,*.hh,*.cxx,*.hxx",
+                         help="Enumerates filename extensions to match C/C++ files [default: %default]")
+    
+    def configure(self, options):
+        self.files = options.__dict__['std.code.cpp.files'].split(',')
+        self.files.sort() # sorted list goes to properties
+        
+    def initialize(self):
+        # trigger version property set
+        core.api.Plugin.initialize(self)
+        db_loader = self.get_plugin_loader().get_database_loader()
+        prev_ext = db_loader.set_property(self.get_name() + ":files", ','.join(self.files))
+        if prev_ext != ','.join(self.files):
+            self.is_updated = True
+
+        self.get_plugin_loader().register_parser(self.files, self)
+        
+    def process(self, parent, data, is_updated):
+        is_updated = is_updated or self.is_updated
+        count_mismatched_brackets = 0
+        if is_updated == True:
+            count_mismatched_brackets = CppCodeParser().run(data)
+        self.notify_children(data, is_updated)
+        return count_mismatched_brackets
+            
+class CppCodeParser(object):
+    
+    regex_cpp = re.compile(r'''
+                   /([\\](?:\n|\r|\r\n))*/(?=\n|\r|\r\n)              # Match C++ style comments (empty comment line)
+                |  /([\\](?:\n|\r|\r\n))*/.*?[^\\](?=\n|\r|\r\n)      # Match C++ style comments
+                                                                      # NOTE: end of line is NOT consumed
+                                                                      # NOTE: ([\\](?:\n|\r|\r\n))* for new line separators,
+                                                                      # Need to support new line separators in expense of efficiency?
+                | /\*\*/                                              # Match C style comments (empty comment line)
+                | /([\\](?:\n|\r|\r\n))*\*.*?\*([\\](?:\n|\r|\r\n))*/ # Match C style comments
+                | \'(?:\\.|[^\\\'])*\'                                # Match quoted strings
+                | "(?:\\.|[^\\"])*"                                   # Match double quoted strings
+                | (((?<=\n|\r)|^)[ \t]*[#].*?[^\\](?=\n|\r|\r\n))     # Match preprocessor
+                                                                      # NOTE: end of line is NOT consumed
+                                                                      # NOTE: beginning of line is NOT consumed
+                | (?P<fn_name>
+                      (operator(                                      # Match C++ operator ...
+                         (\s+[_a-zA-Z][_a-zA-Z0-9]*(\s*\[\s*\])?)     # - cast, new and delete operators
+                       | (\s*\[\s*\])                                 # - operator []
+                       | (\s*\(\s*\))                                 # - operator ()
+                       | (\s*[+-\\*/=<>!%&^|~,?.]{1,3})               # - other operators (from 1 to 3 symbols)
+                      ))                                               
+                    | ([~]?[_a-zA-Z][_a-zA-Z0-9]*)                    # ... or function or constructor
+                  )\s*[(]                                             # LIMITATION: if there are comments after function name
+                                                                      # and before '(', it is not detected
+                                                                      # LIMITATION: if there are comments within operator definition,
+                                                                      # if may be not detected
+                | ((?P<block_type>class|struct|namespace)             # Match C++ class or struct
+                    (?P<block_name>((\s+[a-zA-Z_][a-zA-Z0-9_]*)|(?=\s*[{])))) # noname is supported, symbol '{' is not consumed
+                                                                      # LIMITATION: if there are comments between keyword and name,
+                                                                      # it is not detected
+                | [<>{};:]                                            # Match block start/end, brackets and statement separator
+                | ((?:\n|\r|\r\n)\s*(?:\n|\r|\r\n))                   # Match double empty line
+            ''',
+            re.DOTALL | re.MULTILINE | re.VERBOSE
+        )
+    
+    regex_ln = re.compile(r'(\n)|(\r)|(\r\n)')
+
+    def run(self, data):
+        self.__init__() # Go to initial state if it is called twice
+        return self.parse(data)
+        
+    def finalize_block(self, text, block, block_end):
+        space_match = re.match('^\s*', text[block['start']:block_end], re.MULTILINE)
+        block['start'] += space_match.end() # trim spaces at the beginning
+        block['end'] = block_end
+
+        start_pos = block['start']
+        crc32 = 0
+        for child in block['children']:
+            # exclude children
+            crc32 = binascii.crc32(text[start_pos:child['start']], crc32)
+            start_pos = child['end']
+        block['checksum'] = binascii.crc32(text[start_pos:block['end']], crc32) & 0xffffffff # to match python 3
+        
+    def add_lines_data(self, text, blocks):
+        def add_lines_data_rec(self, text, blocks):
+            for each in blocks:
+                # add line begin
+                self.total_current += len(self.regex_ln.findall(text, self.total_last_pos, each['start']))
+                each['line_begin'] = self.total_current
+                self.total_last_pos = each['start']
+                # process enclosed
+                add_lines_data_rec(self, text, each['children'])
+                # add line end
+                self.total_current += len(self.regex_ln.findall(text, self.total_last_pos, each['end']))
+                each['line_end'] = self.total_current
+                self.total_last_pos = each['end']
+        self.total_last_pos = 0
+        self.total_current = 1
+        add_lines_data_rec(self, text, blocks)
+
+    def add_regions(self, data, blocks):
+        # Note: data.add_region() internals depend on special ordering of regions
+        # in order to identify enclosed regions efficiently
+        def add_regions_rec(self, data, blocks):
+            def get_type_id(data, named_type):
+                if named_type == "function":
+                    return data.get_region_types().FUNCTION
+                elif named_type == "class":
+                    return data.get_region_types().CLASS
+                elif named_type == "struct":
+                    return data.get_region_types().STRUCT
+                elif named_type == "namespace":
+                    return data.get_region_types().NAMESPACE
+                elif named_type == "__global__":
+                    return data.get_region_types().GLOBAL
+                else:
+                    assert(False)
+            for each in blocks:
+                data.add_region(each['name'], each['start'], each['end'],
+                                each['line_begin'], each['line_end'], each['cursor'],
+                                get_type_id(data, each['type']), each['checksum'])
+                add_regions_rec(self, data, each['children'])
+        add_regions_rec(self, data, blocks)
+        
+    def parse(self, data):
+        
+        def reset_next_block(start):
+            return {'name':'', 'start':start, 'cursor':0, 'type':'', 'confirmed':False}
+        
+        count_mismatched_brackets = 0
+        
+        text = data.get_content()
+        indent_current = 0;
+        
+        blocks = [{'name':'__global__', 'start':0, 'cursor':0, 'type':'__global__', 'indent_start':indent_current, 'children':[]}]
+        curblk = 0
+        
+        next_block = reset_next_block(0)
+        
+        cursor_last_pos = 0
+        cursor_current = 1
+        
+        for m in re.finditer(self.regex_cpp, text):
+            # Comment
+            if text[m.start()] == '/':
+                data.add_marker(m.start(), m.end(), data.get_marker_types().COMMENT)
+                if text[m.start():m.end()].startswith("//\n"):
+                    print text[m.start():m.end()]
+            
+            # String
+            elif text[m.start()] == '"' or text[m.start()] == '\'':
+                data.add_marker(m.start() + 1, m.end() - 1, data.get_marker_types().STRING)
+            
+            # Preprocessor (including internal comments)
+            elif text[m.start()] == ' ' or text[m.start()] == '\t' or text[m.start()] == '#':
+                data.add_marker(m.start(), m.end(), data.get_marker_types().PREPROCESSOR)
+
+            # Statement end
+            elif text[m.start()] == ';':
+                # Reset next block name and start
+                next_block['name'] = ""
+                next_block['start'] = m.end() # potential region start
+
+            # Template argument closing bracket
+            elif text[m.start()] == '>':
+                # Reset next block name (in order to skip class names in templates), if has not been confirmed before
+                if next_block['confirmed'] == False and (next_block['type'] == 'class' or next_block['type'] == 'struct'):
+                    next_block['name'] = ""
+                    
+            # Template argument opening bracket or after class inheritance specification
+            elif text[m.start()] == ':' or text[m.start()] == '<':
+                # .. if goes after calss definition
+                if next_block['type'] == 'class' or next_block['type'] == 'struct':
+                    next_block['confirmed'] = True
+
+            # Double end line
+            elif text[m.start()] == '\n' or text[m.start()] == '\r':
+                # Reset next block start, if has not been named yet
+                if next_block['name'] == "":
+                    next_block['start'] = m.end() # potential region start
+
+            # Block start...
+            elif text[m.start()] == '{':
+                # shift indent right
+                indent_current += 1
+                
+                # ... if name detected previously
+                if next_block['name'] != '': # - Start of enclosed block
+                    blocks.append({'name':next_block['name'],
+                                   'start':next_block['start'],
+                                   'cursor':next_block['cursor'],
+                                   'type':next_block['type'],
+                                   'indent_start':indent_current,
+                                   'children':[]})
+                    next_block = reset_next_block(m.end())
+                    curblk += 1
+                # ... reset next block start, otherwise
+                else: # - unknown type of block start
+                    next_block['start'] = m.end() # potential region start
+            
+            # Block end...
+            elif text[m.start()] == '}':
+                # ... if indent level matches the start
+                if blocks[curblk]['indent_start'] == indent_current:
+                    next_block = reset_next_block(m.end())
+                    if curblk == 0:
+                        logging.warning("Non-matching closing bracket '}' detected: " + data.get_path() + ":" +
+                                        str(cursor_current + len(self.regex_ln.findall(text, cursor_last_pos, m.start()))))
+                        count_mismatched_brackets += 1
+                        continue
+                    
+                    self.finalize_block(text, blocks[curblk], m.end())
+                    assert(blocks[curblk]['type'] != '__global__')
+                    
+                    curblk -= 1
+                    assert(curblk >= 0)
+                    blocks[curblk]['children'].append(blocks.pop())
+
+                # shift indent left
+                indent_current -= 1
+                if indent_current < 0:
+                    logging.warning("Non-matching closing bracket '}' detected")
+                    count_mismatched_brackets += 1
+                    indent_current = 0
+
+            # Potential namespace, struct, class
+            elif m.group('block_type') != None:
+                if next_block['name'] == "":
+                    # - 'name'
+                    next_block['name'] = m.group('block_name').strip()
+                    if next_block['name'] == "":
+                        next_block['name'] = '__noname__'
+                    # - 'cursor'
+                    cursor_current += len(self.regex_ln.findall(text, cursor_last_pos, m.start('block_name')))
+                    cursor_last_pos = m.start('block_name')
+                    next_block['cursor'] = cursor_current
+                    # - 'type'
+                    next_block['type'] = m.group('block_type').strip()
+                    # - 'start' detected earlier
+
+            # Potential function name detected...
+            elif m.group('fn_name') != None:
+                # ... if outside of a function (do not detect enclosed functions, unless classes are matched)
+                # wander why 'or next_block['type'] != 'function'' is in the condition?
+                # - remove it, run the tests and will see
+                if blocks[curblk]['type'] != 'function' and (next_block['name'] == "" or next_block['type'] != 'function'):
+                    # - 'name'
+                    next_block['name'] = m.group('fn_name').strip()
+                    # - 'cursor'
+                    cursor_current += len(self.regex_ln.findall(text, cursor_last_pos, m.start('fn_name')))
+                    cursor_last_pos = m.start('fn_name')
+                    # NOTE: cursor could be collected together with line_begin, line_end,
+                    # but we keep it here separately for easier debugging of file parsing problems
+                    next_block['cursor'] = cursor_current
+                    # - 'type'
+                    next_block['type'] = 'function'
+                    # - 'start' detected earlier
+            else:
+                assert(len("Unknown match by regular expression") == 0)
+
+        while indent_current > 0:
+            # log all
+            logging.warning("Non-matching opening bracket '{' detected")
+            count_mismatched_brackets += 1
+            indent_current -= 1
+
+        for (ind, each) in enumerate(blocks):
+            each = each # used
+            block = blocks[len(blocks) - 1 - ind]
+            self.finalize_block(text, block, len(text))
+
+        self.add_lines_data(text, blocks)
+        self.add_regions(data, blocks)
+        
+        return count_mismatched_brackets
+
+            

+ 26 - 0
releases/metrixplusplus-1.1.70/ext/std/code/cs.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ is free software: you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation, version 3 of the License.
+;    
+;    Metrix++ is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;    GNU General Public License for more details.
+;    
+;    You should have received a copy of the GNU General Public License
+;    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.0
+package: std.code
+module:  cs
+class:   Plugin
+depends: None
+enabled: True

+ 319 - 0
releases/metrixplusplus-1.1.70/ext/std/code/cs.py

@@ -0,0 +1,319 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import re
+import binascii
+import logging
+
+import core.api
+
+class Plugin(core.api.Plugin, core.api.Parent, core.api.IParser, core.api.IConfigurable, core.api.ICode):
+    
+    def declare_configuration(self, parser):
+        parser.add_option("--std.code.cs.files", default="*.cs",
+                         help="Enumerates filename extensions to match C# files [default: %default]")
+    
+    def configure(self, options):
+        self.files = options.__dict__['std.code.cs.files'].split(',')
+        self.files.sort() # sorted list goes to properties
+        
+    def initialize(self):
+        # trigger version property set
+        core.api.Plugin.initialize(self)
+        db_loader = self.get_plugin_loader().get_database_loader()
+        prev_ext = db_loader.set_property(self.get_name() + ":files", ','.join(self.files))
+        if prev_ext != ','.join(self.files):
+            self.is_updated = True
+
+        self.get_plugin_loader().register_parser(self.files, self)
+        
+    def process(self, parent, data, is_updated):
+        is_updated = is_updated or self.is_updated
+        count_mismatched_brackets = 0
+        if is_updated == True:
+            count_mismatched_brackets = CsCodeParser().run(data)
+        self.notify_children(data, is_updated)
+        return count_mismatched_brackets
+            
+class CsCodeParser(object):
+    
+    regex_cpp = re.compile(r'''
+                   //(?=\n|\r|\r\n)                                   # Match C# style comments (empty comment line)
+                |  //.*?(?=\n|\r|\r\n)                                # Match C# style comments
+                                                                      # NOTE: end of line is NOT consumed
+                                                                      # NOTE: it is slightly different in C++
+                | /\*\*/                                              # Match C style comments (empty comment line)
+                                                                      # NOTE: it is slightly different in C++
+                | /\*.*?\*/                                           # Match C style comments
+                                                                      # NOTE: it is slightly different in C++
+                | \'(?:\\.|[^\\\'])*\'                                # Match quoted strings
+                | "(?:\\.|[^\\"])*"                                   # Match double quoted strings
+                | (((?<=\n|\r)|^)[ \t]*[#].*?(?=\n|\r|\r\n))          # Match preprocessor
+                                                                      # NOTE: end of line is NOT consumed
+                                                                      # NOTE: beginning of line is NOT consumed
+                                                                      # NOTE: C# does not support backslashing as C++ does
+                | (?P<fn_name>
+                      (operator(                                      # Match C# operator ...
+                         (\s+[_a-zA-Z][_a-zA-Z0-9]*(\s*\[\s*\])?)     # - cast, true, false operators
+                       | (\s*\[\s*\])                                 # - operator []
+                       | (\s*\(\s*\))                                 # - operator ()
+                       | (\s*[+-\\*/=<>!%&^|~,?.]{1,3})               # - other operators (from 1 to 3 symbols)
+                                                                      #   NOTE: maybe dot and ? should not be in the list...
+                      ))                                               
+                    | (([~]\s*)?[_a-zA-Z][_a-zA-Z0-9]*
+                       ([.][a-zA-Z_][a-zA-Z0-9_]*)*)                  # ... or function or constructor
+                                                                      # NOTE: C# destructor can have spaces in name after ~
+                                                                      # NOTE: explicit interface implementation method has got a dot
+                    | (?P<prop_setget>get|set)                        # ... or property setter/getter
+                  )\s*(?(prop_setget)(?=[{])|[(<])
+                                                                      # LIMITATION: if there are comments after function name
+                                                                      # and before '(', it is not detected
+                                                                      # LIMITATION: if there are comments within operator definition,
+                                                                      # if may be not detected
+                                                                      # LIMITATION: if there are comments after set|get keyword,
+                                                                      # if may be not detected
+                | ((?P<block_type>class|struct|namespace|interface)   # Match class or struct or interface or namespace
+                    (?P<block_name>(\s+[a-zA-Z_][a-zA-Z0-9_]*)([.][a-zA-Z_][a-zA-Z0-9_]*)*))
+                                                                      # NOTE: noname instances are impossible in C#
+                                                                      # NOTE: names can have sub-names separated by dots
+                                                                      # LIMITATION: if there are comments between keyword and name,
+                                                                      # it is not detected
+                | [\[\]{};]                                               # Match block start/end and statement separator
+                                                                      # NOTE: C++ parser includes processing of <> and : 
+                                                                      #       to handle template definitions, it is easier in C#
+                | ((?:\n|\r|\r\n)\s*(?:\n|\r|\r\n))                   # Match double empty line
+            ''',
+            re.DOTALL | re.MULTILINE | re.VERBOSE
+        )
+
+    regex_ln = re.compile(r'(\n)|(\r)|(\r\n)')
+
+    def run(self, data):
+        self.__init__() # Go to initial state if it is called twice
+        return self.parse(data)
+        
+    def finalize_block(self, text, block, block_end):
+        space_match = re.match('^\s*', text[block['start']:block_end], re.MULTILINE)
+        block['start'] += space_match.end() # trim spaces at the beginning
+        block['end'] = block_end
+
+        start_pos = block['start']
+        crc32 = 0
+        for child in block['children']:
+            # exclude children
+            crc32 = binascii.crc32(text[start_pos:child['start']], crc32)
+            start_pos = child['end']
+        block['checksum'] = binascii.crc32(text[start_pos:block['end']], crc32) & 0xffffffff # to match python 3
+        
+    def add_lines_data(self, text, blocks):
+        def add_lines_data_rec(self, text, blocks):
+            for each in blocks:
+                # add line begin
+                self.total_current += len(self.regex_ln.findall(text, self.total_last_pos, each['start']))
+                each['line_begin'] = self.total_current
+                self.total_last_pos = each['start']
+                # process enclosed
+                add_lines_data_rec(self, text, each['children'])
+                # add line end
+                self.total_current += len(self.regex_ln.findall(text, self.total_last_pos, each['end']))
+                each['line_end'] = self.total_current
+                self.total_last_pos = each['end']
+        self.total_last_pos = 0
+        self.total_current = 1
+        add_lines_data_rec(self, text, blocks)
+
+    def add_regions(self, data, blocks):
+        # Note: data.add_region() internals depend on special ordering of regions
+        # in order to identify enclosed regions efficiently
+        def add_regions_rec(self, data, blocks):
+            def get_type_id(data, named_type):
+                if named_type == "function":
+                    return data.get_region_types().FUNCTION
+                elif named_type == "class":
+                    return data.get_region_types().CLASS
+                elif named_type == "struct":
+                    return data.get_region_types().STRUCT
+                elif named_type == "namespace":
+                    return data.get_region_types().NAMESPACE
+                elif named_type == "interface":
+                    return data.get_region_types().INTERFACE
+                elif named_type == "__global__":
+                    return data.get_region_types().GLOBAL
+                else:
+                    assert(False)
+            for each in blocks:
+                data.add_region(each['name'], each['start'], each['end'],
+                                each['line_begin'], each['line_end'], each['cursor'],
+                                get_type_id(data, each['type']), each['checksum'])
+                add_regions_rec(self, data, each['children'])
+        add_regions_rec(self, data, blocks)
+        
+    def parse(self, data):
+        
+        def reset_next_block(start):
+            return {'name':'', 'start':start, 'cursor':0, 'type':'', 'inside_attribute':False}
+        
+        count_mismatched_brackets = 0
+        
+        text = data.get_content()
+        indent_current = 0;
+        
+        blocks = [{'name':'__global__', 'start':0, 'cursor':0, 'type':'__global__', 'indent_start':indent_current, 'children':[]}]
+        curblk = 0
+        
+        next_block = reset_next_block(0)
+        
+        cursor_last_pos = 0
+        cursor_current = 1
+        
+        for m in re.finditer(self.regex_cpp, text):
+            # Comment
+            if text[m.start()] == '/':
+                data.add_marker(m.start(), m.end(), data.get_marker_types().COMMENT)
+                if text[m.start():m.end()].startswith("//\n"):
+                    print text[m.start():m.end()]
+            
+            # String
+            elif text[m.start()] == '"' or text[m.start()] == '\'':
+                data.add_marker(m.start() + 1, m.end() - 1, data.get_marker_types().STRING)
+            
+            # Preprocessor (including internal comments)
+            elif text[m.start()] == ' ' or text[m.start()] == '\t' or text[m.start()] == '#':
+                data.add_marker(m.start(), m.end(), data.get_marker_types().PREPROCESSOR)
+
+            # Statement end
+            elif text[m.start()] == ';':
+                # Reset next block name and start
+                next_block['name'] = ""
+                next_block['start'] = m.end() # potential region start
+
+            # Block openned by '[' bracket...
+            elif text[m.start()] == '[':
+                # ... may include attributes, so do not capture function names inside
+                next_block['inside_attribute'] = True
+
+            # Block closed by ']' bracket...
+            # note: do not care about nesting for simplicity -
+            #       because attribute's statement can not have symbol ']' inside 
+            elif text[m.start()] == ']':
+                # ... may include attributes, so do not capture function names inside
+                next_block['inside_attribute'] = False
+
+            # Double end line
+            elif text[m.start()] == '\n' or text[m.start()] == '\r':
+                # Reset next block start, if has not been named yet
+                if next_block['name'] == "":
+                    next_block['start'] = m.end() # potential region start
+
+            # Block start...
+            elif text[m.start()] == '{':
+                # shift indent right
+                indent_current += 1
+                
+                # ... if name detected previously
+                if next_block['name'] != '': # - Start of enclosed block
+                    blocks.append({'name':next_block['name'],
+                                   'start':next_block['start'],
+                                   'cursor':next_block['cursor'],
+                                   'type':next_block['type'],
+                                   'indent_start':indent_current,
+                                   'children':[]})
+                    next_block = reset_next_block(m.end())
+                    curblk += 1
+                # ... reset next block start, otherwise
+                else: # - unknown type of block start
+                    next_block['start'] = m.end() # potential region start
+            
+            # Block end...
+            elif text[m.start()] == '}':
+                # ... if indent level matches the start
+                if blocks[curblk]['indent_start'] == indent_current:
+                    next_block = reset_next_block(m.end())
+                    if curblk == 0:
+                        logging.warning("Non-matching closing bracket '}' detected: " + data.get_path() + ":" +
+                                        str(cursor_current + len(self.regex_ln.findall(text, cursor_last_pos, m.start()))))
+                        count_mismatched_brackets += 1
+                        continue
+                    
+                    self.finalize_block(text, blocks[curblk], m.end())
+                    assert(blocks[curblk]['type'] != '__global__')
+                    
+                    curblk -= 1
+                    assert(curblk >= 0)
+                    blocks[curblk]['children'].append(blocks.pop())
+
+                # shift indent left
+                indent_current -= 1
+                if indent_current < 0:
+                    logging.warning("Non-matching closing bracket '}' detected")
+                    count_mismatched_brackets += 1
+                    indent_current = 0
+
+            # Potential namespace, struct, class, interface
+            elif m.group('block_type') != None:
+                if next_block['name'] == "":
+                    # - 'name'
+                    next_block['name'] = m.group('block_name').strip()
+                    # - 'cursor'
+                    cursor_current += len(self.regex_ln.findall(text, cursor_last_pos, m.start('block_name')))
+                    cursor_last_pos = m.start('block_name')
+                    next_block['cursor'] = cursor_current
+                    # - 'type'
+                    next_block['type'] = m.group('block_type').strip()
+                    # - 'start' detected earlier
+
+            # Potential function name detected...
+            elif m.group('fn_name') != None:
+                # ... if outside of a function
+                #     (do not detect functions enclosed directly in a function, i.e. without classes)
+                # ... and other name before has not been matched 
+                if blocks[curblk]['type'] != 'function' and (next_block['name'] == "") \
+                       and next_block['inside_attribute'] == False:
+                    # - 'name'
+                    next_block['name'] = m.group('fn_name').strip()
+                    # - 'cursor'
+                    cursor_current += len(self.regex_ln.findall(text, cursor_last_pos, m.start('fn_name')))
+                    cursor_last_pos = m.start('fn_name')
+                    # NOTE: cursor could be collected together with line_begin, line_end,
+                    # but we keep it here separately for easier debugging of file parsing problems
+                    next_block['cursor'] = cursor_current
+                    # - 'type'
+                    next_block['type'] = 'function'
+                    # - 'start' detected earlier
+            else:
+                assert(len("Unknown match by regular expression") == 0)
+
+        while indent_current > 0:
+            # log all
+            logging.warning("Non-matching opening bracket '{' detected")
+            count_mismatched_brackets += 1
+            indent_current -= 1
+
+        for (ind, each) in enumerate(blocks):
+            each = each # used
+            block = blocks[len(blocks) - 1 - ind]
+            self.finalize_block(text, block, len(text))
+
+        self.add_lines_data(text, blocks)
+        self.add_regions(data, blocks)
+        
+        return count_mismatched_brackets
+
+            

+ 26 - 0
releases/metrixplusplus-1.1.70/ext/std/code/java.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ is free software: you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation, version 3 of the License.
+;    
+;    Metrix++ is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;    GNU General Public License for more details.
+;    
+;    You should have received a copy of the GNU General Public License
+;    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.1
+package: std.code
+module:  java
+class:   Plugin
+depends: None
+enabled: True

+ 278 - 0
releases/metrixplusplus-1.1.70/ext/std/code/java.py

@@ -0,0 +1,278 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import re
+import binascii
+import logging
+
+import core.api
+
+class Plugin(core.api.Plugin, core.api.Parent, core.api.IParser, core.api.IConfigurable, core.api.ICode):
+    
+    def declare_configuration(self, parser):
+        parser.add_option("--std.code.java.files", default="*.java",
+                         help="Enumerates filename extensions to match Java files [default: %default]")
+    
+    def configure(self, options):
+        self.files = options.__dict__['std.code.java.files'].split(',')
+        self.files.sort() # sorted list goes to properties
+        
+    def initialize(self):
+        # trigger version property set
+        core.api.Plugin.initialize(self)
+        db_loader = self.get_plugin_loader().get_database_loader()
+        prev_ext = db_loader.set_property(self.get_name() + ":files", ','.join(self.files))
+        if prev_ext != ','.join(self.files):
+            self.is_updated = True
+
+        self.get_plugin_loader().register_parser(self.files, self)
+        
+    def process(self, parent, data, is_updated):
+        is_updated = is_updated or self.is_updated
+        count_mismatched_brackets = 0
+        if is_updated == True:
+            count_mismatched_brackets = JavaCodeParser().run(data)
+        self.notify_children(data, is_updated)
+        return count_mismatched_brackets
+            
+class JavaCodeParser(object):
+    
+    regex_cpp = re.compile(r'''
+                   //(?=\n|\r|\r\n)                                   # Match Java style comments (empty comment line)
+                |  //.*?(?=\n|\r|\r\n)                                # Match Java style comments
+                                                                      # NOTE: end of line is NOT consumed
+                                                                      # NOTE: it is slightly different in C++
+                | /\*\*/                                              # Match C style comments (empty comment line)
+                                                                      # NOTE: it is slightly different in C++
+                | /\*.*?\*/                                           # Match C style comments
+                                                                      # NOTE: it is slightly different in C++
+                | \'(?:\\.|[^\\\'])*\'                                # Match quoted strings
+                | "(?:\\.|[^\\"])*"                                   # Match double quoted strings
+                | (?P<fn_name>([@]?[_$a-zA-Z][_$a-zA-Z0-9]*))\s*[(]   # Match function
+                                                                      # NOTE: Matches attributes which are excluded later
+                                                                      # NOTE: Java may include $ in the name
+                                                                      # LIMITATION: if there are comments after function name
+                                                                      # and before '(', it is not detected
+                | ((?P<block_type>class|interface)                    # Match class or namespace
+                    (?P<block_name>(\s+[_$a-zA-Z][_$a-zA-Z0-9]*)))
+                                                                      # NOTE: noname instances are impossible in Java
+                                                                      # LIMITATION: if there are comments between keyword and name,
+                                                                      # it is not detected
+                | [{};]                                               # Match block start/end and statement separator
+                                                                      # NOTE: C++ parser includes processing of <> and : 
+                                                                      #       to handle template definitions, it is easier in Java
+                | ((?:\n|\r|\r\n)\s*(?:\n|\r|\r\n))                   # Match double empty line
+            ''',
+            re.DOTALL | re.MULTILINE | re.VERBOSE
+        )
+
+    regex_ln = re.compile(r'(\n)|(\r)|(\r\n)')
+
+    def run(self, data):
+        self.__init__() # Go to initial state if it is called twice
+        return self.parse(data)
+        
+    def finalize_block(self, text, block, block_end):
+        space_match = re.match('^\s*', text[block['start']:block_end], re.MULTILINE)
+        block['start'] += space_match.end() # trim spaces at the beginning
+        block['end'] = block_end
+
+        start_pos = block['start']
+        crc32 = 0
+        for child in block['children']:
+            # exclude children
+            crc32 = binascii.crc32(text[start_pos:child['start']], crc32)
+            start_pos = child['end']
+        block['checksum'] = binascii.crc32(text[start_pos:block['end']], crc32) & 0xffffffff # to match python 3
+        
+    def add_lines_data(self, text, blocks):
+        def add_lines_data_rec(self, text, blocks):
+            for each in blocks:
+                # add line begin
+                self.total_current += len(self.regex_ln.findall(text, self.total_last_pos, each['start']))
+                each['line_begin'] = self.total_current
+                self.total_last_pos = each['start']
+                # process enclosed
+                add_lines_data_rec(self, text, each['children'])
+                # add line end
+                self.total_current += len(self.regex_ln.findall(text, self.total_last_pos, each['end']))
+                each['line_end'] = self.total_current
+                self.total_last_pos = each['end']
+        self.total_last_pos = 0
+        self.total_current = 1
+        add_lines_data_rec(self, text, blocks)
+
+    def add_regions(self, data, blocks):
+        # Note: data.add_region() internals depend on special ordering of regions
+        # in order to identify enclosed regions efficiently
+        def add_regions_rec(self, data, blocks):
+            def get_type_id(data, named_type):
+                if named_type == "function":
+                    return data.get_region_types().FUNCTION
+                elif named_type == "class":
+                    return data.get_region_types().CLASS
+                elif named_type == "interface":
+                    return data.get_region_types().INTERFACE
+                elif named_type == "__global__":
+                    return data.get_region_types().GLOBAL
+                else:
+                    assert(False)
+            for each in blocks:
+                data.add_region(each['name'], each['start'], each['end'],
+                                each['line_begin'], each['line_end'], each['cursor'],
+                                get_type_id(data, each['type']), each['checksum'])
+                add_regions_rec(self, data, each['children'])
+        add_regions_rec(self, data, blocks)
+        
+    def parse(self, data):
+        
+        def reset_next_block(start):
+            return {'name':'', 'start':start, 'cursor':0, 'type':''}
+        
+        count_mismatched_brackets = 0
+        
+        text = data.get_content()
+        indent_current = 0;
+        
+        blocks = [{'name':'__global__', 'start':0, 'cursor':0, 'type':'__global__', 'indent_start':indent_current, 'children':[]}]
+        curblk = 0
+        
+        next_block = reset_next_block(0)
+        
+        cursor_last_pos = 0
+        cursor_current = 1
+        
+        for m in re.finditer(self.regex_cpp, text):
+            # Comment
+            if text[m.start()] == '/':
+                data.add_marker(m.start(), m.end(), data.get_marker_types().COMMENT)
+                if text[m.start():m.end()].startswith("//\n"):
+                    print text[m.start():m.end()]
+            
+            # String
+            elif text[m.start()] == '"' or text[m.start()] == '\'':
+                data.add_marker(m.start() + 1, m.end() - 1, data.get_marker_types().STRING)
+            
+            # Statement end
+            elif text[m.start()] == ';':
+                # Reset next block name and start
+                next_block['name'] = ""
+                next_block['start'] = m.end() # potential region start
+
+            # Double end line
+            elif text[m.start()] == '\n' or text[m.start()] == '\r':
+                # Reset next block start, if has not been named yet
+                if next_block['name'] == "":
+                    next_block['start'] = m.end() # potential region start
+
+            # Block start...
+            elif text[m.start()] == '{':
+                # shift indent right
+                indent_current += 1
+                
+                # ... if name detected previously
+                if next_block['name'] != '': # - Start of enclosed block
+                    blocks.append({'name':next_block['name'],
+                                   'start':next_block['start'],
+                                   'cursor':next_block['cursor'],
+                                   'type':next_block['type'],
+                                   'indent_start':indent_current,
+                                   'children':[]})
+                    next_block = reset_next_block(m.end())
+                    curblk += 1
+                # ... reset next block start, otherwise
+                else: # - unknown type of block start
+                    next_block['start'] = m.end() # potential region start
+            
+            # Block end...
+            elif text[m.start()] == '}':
+                # ... if indent level matches the start
+                if blocks[curblk]['indent_start'] == indent_current:
+                    next_block = reset_next_block(m.end())
+                    if curblk == 0:
+                        logging.warning("Non-matching closing bracket '}' detected: " + data.get_path() + ":" +
+                                        str(cursor_current + len(self.regex_ln.findall(text, cursor_last_pos, m.start()))))
+                        count_mismatched_brackets += 1
+                        continue
+                    
+                    self.finalize_block(text, blocks[curblk], m.end())
+                    assert(blocks[curblk]['type'] != '__global__')
+                    
+                    curblk -= 1
+                    assert(curblk >= 0)
+                    blocks[curblk]['children'].append(blocks.pop())
+
+                # shift indent left
+                indent_current -= 1
+                if indent_current < 0:
+                    logging.warning("Non-matching closing bracket '}' detected")
+                    count_mismatched_brackets += 1
+                    indent_current = 0
+
+            # Potential class, interface
+            elif m.group('block_type') != None:
+                if next_block['name'] == "":
+                    # - 'name'
+                    next_block['name'] = m.group('block_name').strip()
+                    # - 'cursor'
+                    cursor_current += len(self.regex_ln.findall(text, cursor_last_pos, m.start('block_name')))
+                    cursor_last_pos = m.start('block_name')
+                    next_block['cursor'] = cursor_current
+                    # - 'type'
+                    next_block['type'] = m.group('block_type').strip()
+                    # - 'start' detected earlier
+
+            # Potential function name detected...
+            elif m.group('fn_name') != None:
+                # ... if outside of a function
+                #     (do not detect functions enclosed directly in a function, i.e. without classes)
+                # ... and other name before has not been matched 
+                if blocks[curblk]['type'] != 'function' and (next_block['name'] == "") and m.group('fn_name')[0] != '@':
+                    # - 'name'
+                    next_block['name'] = m.group('fn_name').strip()
+                    # - 'cursor'
+                    cursor_current += len(self.regex_ln.findall(text, cursor_last_pos, m.start('fn_name')))
+                    cursor_last_pos = m.start('fn_name')
+                    # NOTE: cursor could be collected together with line_begin, line_end,
+                    # but we keep it here separately for easier debugging of file parsing problems
+                    next_block['cursor'] = cursor_current
+                    # - 'type'
+                    next_block['type'] = 'function'
+                    # - 'start' detected earlier
+            else:
+                assert(len("Unknown match by regular expression") == 0)
+
+        while indent_current > 0:
+            # log all
+            logging.warning("Non-matching opening bracket '{' detected")
+            count_mismatched_brackets += 1
+            indent_current -= 1
+
+        for (ind, each) in enumerate(blocks):
+            each = each # used
+            block = blocks[len(blocks) - 1 - ind]
+            self.finalize_block(text, block, len(text))
+
+        self.add_lines_data(text, blocks)
+        self.add_regions(data, blocks)
+        
+        return count_mismatched_brackets
+
+            

+ 26 - 0
releases/metrixplusplus-1.1.70/ext/std/code/test.ini

@@ -0,0 +1,26 @@
+;
+;    Metrix++, Copyright 2009-2013, Metrix++ Project
+;    Link: http://metrixplusplus.sourceforge.net
+;    
+;    This file is a part of Metrix++ Tool.
+;    
+;    Metrix++ is free software: you can redistribute it and/or modify
+;    it under the terms of the GNU General Public License as published by
+;    the Free Software Foundation, version 3 of the License.
+;    
+;    Metrix++ is distributed in the hope that it will be useful,
+;    but WITHOUT ANY WARRANTY; without even the implied warranty of
+;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;    GNU General Public License for more details.
+;    
+;    You should have received a copy of the GNU General Public License
+;    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+;
+
+[Plugin]
+version: 1.0
+package: std.code
+module:  test
+class:   Plugin
+depends: None
+enabled: True

+ 37 - 0
releases/metrixplusplus-1.1.70/ext/std/code/test.py

@@ -0,0 +1,37 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import core.api
+
+
+# used for testing and development purposes
+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)
+
+    def callback(self, parent, data, is_updated):
+
+        text = data.get_content()
+        for region in data.iterate_regions():
+            print "Region:", region.get_name(), region.get_cursor()
+            for marker in data.iterate_markers(region_id=region.get_id(), exclude_children = True):
+                print "\tMarker:", data.get_marker_types()().to_str(marker.get_type()), text[marker.get_offset_begin():marker.get_offset_end()]

+ 75 - 0
releases/metrixplusplus-1.1.70/metrixpp.py

@@ -0,0 +1,75 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import time
+import sys
+import logging
+import os
+import subprocess
+import itertools
+
+import core.log
+
+def main():
+    
+    os.environ['METRIXPLUSPLUS_INSTALL_DIR'] = os.path.dirname(os.path.abspath(__file__))
+    
+    available_tools = []
+    for fname in os.listdir(os.path.join(os.environ['METRIXPLUSPLUS_INSTALL_DIR'], 'tools')):
+        tool_name = os.path.splitext(fname)[0]
+        if tool_name == '__init__':
+            continue
+        if tool_name not in available_tools:
+            available_tools.append(tool_name)
+
+    exemode = None
+    if len(sys.argv[1:]) != 0:
+        exemode = sys.argv[1]
+    if exemode != "-R" and exemode != "-D":
+        exemode = '-D' # TODO implement install and release mode
+        # inject '-D' or '-R' option
+        exit(subprocess.call(itertools.chain([sys.executable, sys.argv[0], '-D'], sys.argv[1:])))
+
+    command = ""
+    if len(sys.argv[1:]) > 1:
+        command = sys.argv[2]
+        
+    if command not in available_tools:
+        logging.error("Unknown action: " + str(command))
+        print "Usage: %prog <action> --help"
+        print "   or: %prog <action> [options] -- [path 1] ... [path N]"
+        print "where: actions are:"
+        for each in available_tools:
+            print "\t" + each
+        return 1
+
+    tool = __import__('tools', globals(), locals(), [command], -1)
+    module_attr = tool.__getattribute__(command)
+    class_attr = module_attr.__getattribute__('Tool')
+    instance = class_attr.__new__(class_attr)
+    instance.__init__()
+    return instance.run(sys.argv[3:])
+            
+if __name__ == '__main__':
+    ts = time.time()
+    core.log.set_default_format()
+    exit_code = main()
+    logging.warning("Exit code: " + str(exit_code) + ". Time spent: " + str(round((time.time() - ts), 2)) + " seconds. Done")
+    exit(exit_code)

+ 50 - 0
releases/metrixplusplus-1.1.70/readme.txt

@@ -0,0 +1,50 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+===============================================================================
+
+WARNING: Metrix++ Project does not use svn repository,
+         offered by the new souceforge platform.
+         Instead, it uses the old repository located here:
+         https://metrixplusplus.svn.sourceforge.net/svnroot/metrixplusplus/
+
+    Metrix++ is a powerful engine for code metrics collection,
+    reporting and analysis and a set of plugins:
+     - parsers for programming languages,
+     - metrics counters,
+     - post processing tools.
+
+    Check change log for the information about new features and defect fixes:
+     - http://metrixplusplus.svn.sourceforge.net/viewvc/metrixplusplus/mainline/changelog.txt
+
+    Check projects documentation for additional information:
+     - http://metrixplusplus.sourceforge.net
+     - ./doc folder within the distributive
+
+    Bug reporting, feature requests and feedback:
+     - e-mail to project administrator: avkonst@users.sourceforge.net
+
+    Follow the Project / Subscribe to Updates:
+     - Click 'Subscribe to Updates' button on sourceforge:
+       https://sourceforge.net/projects/metrixplusplus/
+
+    ---
+    Thank you for using the tool!
+
+===============================================================================

+ 18 - 0
releases/metrixplusplus-1.1.70/tests/__init__.py

@@ -0,0 +1,18 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#

+ 255 - 0
releases/metrixplusplus-1.1.70/tests/common.py

@@ -0,0 +1,255 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+import inspect
+import os.path
+import subprocess
+import logging
+import difflib
+import unittest
+import shutil
+
+class ToolRunner(object):
+
+    def __init__(self,
+                 tool_name,
+                 opts_list = [],
+                 dirs_list = None,
+                 cwd='sources',
+                 prefix = "default",
+                 exit_code = 0,
+                 save_prev = False,
+                 use_prev = False,
+                 check_stderr = None,
+                 remove_exiting_dbfile = None,
+                 remove_exiting_dbfile_prev = False):
+        
+        self.message = ""
+        
+        # identify gold_file_location
+        curframe = inspect.currentframe()
+        calframe = inspect.getouterframes(curframe, 2)
+        test_name = calframe[1][3]
+        suite_name = os.path.splitext(os.path.basename(calframe[1][1]))[0]
+        group_name = os.path.basename(os.path.dirname(calframe[1][1]))
+
+        self.suite_location = os.path.join('tests', group_name, suite_name) 
+        self.test_location = os.path.join(self.suite_location, test_name + "_" + tool_name + "_" + str(prefix))
+
+        db_file = os.path.join(os.environ['METRIXPLUSPLUS_INSTALL_DIR'], self.suite_location, test_name + ".db")
+        self.dbfile = db_file
+        if (remove_exiting_dbfile == True or (remove_exiting_dbfile == None and tool_name == 'collect')) and os.path.exists(db_file):
+            os.unlink(db_file)
+        
+        db_file_prev = os.path.join(os.environ['METRIXPLUSPLUS_INSTALL_DIR'], self.suite_location, test_name + ".prev.db")
+        self.dbfile_prev = db_file_prev
+        if (remove_exiting_dbfile_prev == True or (remove_exiting_dbfile_prev == None and tool_name == 'collect')) and os.path.exists(db_file_prev):
+            os.unlink(db_file_prev)
+
+        self.cwd = cwd
+
+        db_opts = ['--general.db-file=' + db_file]
+        if use_prev == True:
+            db_opts.append('--general.db-file-prev=' + db_file_prev)
+        self.dbopts = db_opts
+        
+        self.dirs_list = [] 
+        if dirs_list != None:
+            for each in dirs_list:
+                self.dirs_list.append(each)
+               
+        self.call_args = ['python', os.path.join(os.environ['METRIXPLUSPLUS_INSTALL_DIR'], "metrixpp.py"), tool_name] \
+                    + db_opts + opts_list + ['--'] + self.dirs_list
+        self.cmd = " ".join(self.call_args)
+        self.exit_code_expected = exit_code
+        self.stderr_lines = check_stderr
+        self.save_prev = save_prev
+        
+    def run(self):
+        logging.debug(self.get_description())
+        child = subprocess.Popen(self.call_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                                 cwd=os.path.join(self.suite_location, self.cwd))
+        (child_stdout, child_stderr) =  child.communicate()
+        self.exit_code = child.returncode
+
+        gold_file_stdout = self.test_location + "_stdout.gold.txt"
+        real_file_stdout = self.test_location + "_stdout.real.txt"
+        diff_file_stdout = self.test_location + "_stdout.diff.html"
+        gold_file_stderr = self.test_location + "_stderr.gold.txt"
+        real_file_stderr = self.test_location + "_stderr.real.txt"
+        diff_file_stderr = self.test_location + "_stderr.diff.html"
+
+        # Regenerate gold files if it was requested
+        if os.environ['METRIXPLUSPLUS_TEST_GENERATE_GOLDS'] == "True":
+            f = open(gold_file_stdout, 'wb');
+            f.write(child_stdout);
+            f.close()
+            if self.stderr_lines != None:
+                f = open(gold_file_stderr, 'wb');
+                f.write(child_stderr);
+                f.close()
+
+        # Match with gold        
+        self.is_stdout_matched = self.inetrnal_compare_with_gold(child_stdout, gold_file_stdout, real_file_stdout, diff_file_stdout)
+        if self.stderr_lines != None:
+            self.is_stderr_matched = self.inetrnal_compare_with_gold(child_stderr, gold_file_stderr, real_file_stderr, diff_file_stderr, self.stderr_lines)
+        else:
+            self.is_stderr_matched = None
+            if self.is_stdout_matched == False:
+                f = open(real_file_stderr, 'wb');
+                f.write(child_stderr);
+                f.close()
+            else:
+                if os.path.exists(real_file_stderr):
+                    os.unlink(real_file_stderr)
+
+
+        if self.save_prev == True:
+            shutil.copy2(self.dbfile, self.dbfile_prev)                
+        return self
+
+    def inetrnal_compare_with_gold(self, text, gold_file, real_file, diff_file, lines = None):
+        if os.path.exists(gold_file) == False:
+            self.message += "\nGold file does not exist: " + gold_file
+            return False
+        
+        f = open(gold_file, 'rb');
+        gold_text = f.read();
+        f.close()
+
+        gold_to_compare = gold_text
+        text_to_compare = text
+        if lines != None:
+            gold_to_compare = ""
+            text_to_compare = ""
+            gold_lines = gold_text.splitlines(True)
+            text_lines = text.splitlines(True)
+            for each in lines:
+                gold_to_compare += "".join(gold_lines[each[0] : each[1]])
+                text_to_compare += "".join(text_lines[each[0] : each[1]])
+            
+        result = (gold_to_compare == text_to_compare)
+        
+        if result == False:
+            f = open(real_file, 'wb');
+            f.write(text);
+            f.close()
+            
+            diff_text = difflib.HtmlDiff().make_file(gold_to_compare.splitlines(), text_to_compare.splitlines(), "Gold file", "Real output")
+            f = open(diff_file, 'w');
+            f.write(diff_text);
+            f.close()
+        else:
+            if os.path.exists(real_file):
+                os.unlink(real_file)
+            if os.path.exists(diff_file):
+                os.unlink(diff_file)
+        return result 
+    
+    def check_exit_code(self):
+        return self.exit_code == self.exit_code_expected
+
+    def check_stdout(self):
+        return self.is_stdout_matched
+
+    def check_stderr(self):
+        if self.is_stderr_matched == None:
+            return True
+        return self.is_stderr_matched
+
+    def check_all(self):
+        result = self.check_exit_code() and self.check_stdout() and self.check_stderr()
+        if result == False:
+            self.message += "\nCheck for exit code: " + str(self.check_exit_code()) \
+             + ", gold: " + str(self.exit_code_expected)  + ", real: " + str(self.exit_code) + \
+             "\nCheck for stdout: " + str(self.check_stdout()) + "\nCheck for stderr: " + str(self.check_stderr())
+        return result
+    
+    def get_message(self):
+        return self.message
+    
+    def get_cmd(self):
+        return self.cmd    
+
+    def get_description(self):
+        return self.get_message() + "\nProcess: " + self.get_cmd()  + "\nCWD: " + os.path.join(self.suite_location, self.cwd)       
+
+    def get_dbfile(self):
+        return self.dbfile
+
+    def get_dbfile_prev(self):
+        return self.dbfile_prev
+    
+class TestCase(unittest.TestCase):
+
+    def get_content_paths(self, cwd='sources'): 
+        curframe = inspect.currentframe()
+        calframe = inspect.getouterframes(curframe, 2)
+        test_name = calframe[1][3]
+        suite_name = os.path.splitext(os.path.basename(calframe[1][1]))[0]
+        group_name = os.path.basename(os.path.dirname(calframe[1][1]))
+        
+        class ContentPaths(object):
+            
+            def __init__(self, cwd, dbfile, dbfile_prev):
+                self.cwd = cwd
+                self.dbfile = dbfile
+                self.dbfile_prev = dbfile_prev
+                
+        return ContentPaths(os.path.join('tests', group_name, suite_name, cwd),
+                            os.path.join(os.environ['METRIXPLUSPLUS_INSTALL_DIR'], 'tests', group_name, suite_name, test_name + ".db"),
+                            os.path.join(os.environ['METRIXPLUSPLUS_INSTALL_DIR'], 'tests', group_name, suite_name, test_name + ".prev.db"))
+
+    def setUp(self):
+        unittest.TestCase.setUp(self)
+
+        logging.basicConfig(format="[TEST-LOG]: %(levelname)s:\t%(message)s", level=logging.WARN)
+        log_level = os.environ['METRIXPLUSPLUS_LOG_LEVEL']
+        if log_level == 'ERROR':
+            log_level = logging.ERROR
+        elif log_level == 'WARNING':
+            log_level = logging.WARNING
+        elif log_level == 'INFO':
+            log_level = logging.INFO
+        elif log_level == 'DEBUG':
+            log_level = logging.DEBUG
+        else:
+            raise AssertionError("Unhandled choice of log level")
+        logging.getLogger().setLevel(log_level)
+        
+        self.runners = []
+        
+    def assertExec(self, runner):
+        # keep reference, so files are not removed during test case time
+        self.runners.append(runner)
+        self.assertTrue(runner.check_all(), runner.get_description())
+        
+    def run(self, result=None):
+        self.current_result = result # remember result for use in tearDown
+        unittest.TestCase.run(self, result)
+
+    def tearDown(self):
+        unittest.TestCase.tearDown(self)
+        if self.current_result.wasSuccessful() == True:
+            for each in self.runners:
+                if each.check_all() == True:
+                    if os.path.exists(each.get_dbfile()):
+                        os.unlink(each.get_dbfile())
+                    if os.path.exists(each.get_dbfile_prev()):
+                        os.unlink(each.get_dbfile_prev())

+ 186 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic.py

@@ -0,0 +1,186 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import unittest
+
+import tests.common
+
+class Test(tests.common.TestCase):
+
+    def test_workflow(self):
+        
+        # first collection
+        runner = tests.common.ToolRunner('collect',
+                                         ['--std.code.complexity.on',
+                                          '--general.log-level=INFO'],
+                                         check_stderr=[(0, -1)],
+                                         save_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export',
+                                         ['--general.log-level=INFO', '--general.format=xml'],
+                                         check_stderr=[(0, -1)])
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit',
+                                         ['--general.log-level=INFO',
+                                          '--general.max-limit=std.code.complexity:cyclomatic:0'],
+                                         check_stderr=[(0, -1)],
+                                         exit_code=4)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('info',
+                                         ['--general.log-level=INFO'],
+                                         check_stderr=[(0, -1)],
+                                         exit_code=0)
+        self.assertExec(runner.run())
+
+        # second collection
+        runner = tests.common.ToolRunner('collect',
+                                         ['--std.code.complexity.on',
+                                          '--general.log-level=INFO'],
+                                         check_stderr=[(0, -1)],
+                                         prefix='second',
+                                         cwd="sources_changed",
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export',
+                                         ['--general.log-level=INFO', '--general.format=xml'],
+                                         check_stderr=[(0, -1)],
+                                         prefix='second',
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export',
+                                         ['--general.log-level=INFO', '--general.format=xml'],
+                                         check_stderr=[(0, -1)],
+                                         prefix='second_per_file',
+                                         dirs_list=['./simple.cpp'],
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit',
+                                         ['--general.log-level=INFO',
+                                          '--general.max-limit=std.code.complexity:cyclomatic:0'],
+                                         check_stderr=[(0, -1)],
+                                         exit_code=6,
+                                         prefix='second',
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit',
+                                         ['--general.log-level=INFO',
+                                          '--general.max-limit=std.code.complexity:cyclomatic:0',
+                                          '--general.warn=all'],
+                                         check_stderr=[(0, -1)],
+                                         exit_code=6,
+                                         prefix='second_warn_all',
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit',
+                                         ['--general.log-level=INFO',
+                                          '--general.max-limit=std.code.complexity:cyclomatic:0',
+                                          '--general.warn=touched'],
+                                         check_stderr=[(0, -1)],
+                                         exit_code=4,
+                                         prefix='second_warn_touched',
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit',
+                                         ['--general.log-level=INFO',
+                                          '--general.max-limit=std.code.complexity:cyclomatic:0',
+                                          '--general.warn=trend'],
+                                         check_stderr=[(0, -1)],
+                                         exit_code=3,
+                                         prefix='second_warn_trend',
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit',
+                                         ['--general.log-level=INFO',
+                                          '--general.max-limit=std.code.complexity:cyclomatic:0',
+                                          '--general.warn=new'],
+                                         check_stderr=[(0, -1)],
+                                         exit_code=2,
+                                         prefix='second_warn_new',
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('info',
+                                         ['--general.log-level=INFO'],
+                                         check_stderr=[(0, -1)],
+                                         prefix='second',
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+    def test_help(self):
+        
+        runner = tests.common.ToolRunner('collect', ['--help'])
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export', ['--help'])
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit', ['--help'])
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('info', ['--help'])
+        self.assertExec(runner.run())
+
+    def test_export_format(self):
+
+        runner = tests.common.ToolRunner('collect', ['--std.code.complexity.on'], save_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export', ['--general.format=txt'], prefix='txt')
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export', ['--general.format=python'], prefix='python')
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export', ['--general.format=xml'], prefix='xml')
+        self.assertExec(runner.run())
+        
+        runner = tests.common.ToolRunner('collect',
+                                         ['--std.code.complexity.on'],
+                                         prefix='nest',
+                                         cwd="sources_changed",
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export',
+                                         ['--general.nest-regions', '--general.format=xml'],
+                                         prefix='nest',
+                                         use_prev=True)
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export',
+                                         ['--general.nest-regions', '--general.format=xml'],
+                                         prefix='nest_per_file',
+                                         dirs_list=['./simple.cpp'],
+                                         use_prev=True)
+        self.assertExec(runner.run())
+        
+
+if __name__ == '__main__':
+    unittest.main()

+ 0 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/sources/.unused.cpp


+ 0 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/sources/dummy.txt


+ 51 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/sources/simple.cpp

@@ -0,0 +1,51 @@
+
+
+// Just produce any code in order to test basic workflow
+namespace hmm
+{
+
+class A
+{
+
+	A()
+	{
+		/* first funtion */
+		this->m_member = 10;
+		if (a & b)
+		{
+			for (int i = 0; i < 0 && i > 0; i++)
+			{
+				// crazy, right?
+			}
+		}
+	}
+
+	int func(int param = 5)
+	{
+		class embeded
+		{
+			embeded()
+			{
+				int a = 10;
+				if (true)
+				{
+					// again crazy
+				}
+			}
+		};
+		if (a);
+	}
+
+	int never(int how_long = 999)
+	{
+		while(true)
+		{
+
+		}
+		return 1;
+	}
+
+	int m_member = 10;
+};
+
+}

+ 0 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/sources_changed/.unused.cpp


+ 64 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/sources_changed/simple.cpp

@@ -0,0 +1,64 @@
+
+
+// Just produce any code in order to test basic workflow
+namespace hmm
+{
+
+class A
+{
+
+	A()
+	{
+		/* first funtion */
+		this->m_member = 10;
+		if (a & b)
+		{
+			for (int i = 0; i < 0 && i > 0; i++)
+			{
+				// crazy, right?
+			}
+		}
+	}
+
+	int func(int param = 5)
+	{
+		class embeded
+		{
+			embeded()
+			{
+				int a = 10;
+				if (true)
+				{
+					// again crazy
+				}
+
+				while (a == b); // regressed
+			}
+		};
+		if (a);
+	}
+
+	int never(int how_long = 999)
+	{
+		while(true)
+		{
+
+		}
+		return 2; // slightly modified
+	}
+
+	char new_func()
+	{
+		// simple
+	}
+
+	char new_func_complex()
+	{
+		if (true) {}
+		return 'A';
+	}
+
+	int m_member = 10;
+};
+
+}

+ 25 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/sources_changed/simple2.cpp

@@ -0,0 +1,25 @@
+
+
+// Just produce any code in order to test basic workflow
+namespace hmm
+{
+
+class New_A
+{
+
+	New_A()
+	{
+		/* first funtion */
+		this->m_member = 10;
+		if (a & b)
+		{
+			for (int i = 0; i < 0 && i > 0; i++)
+			{
+				// crazy, right?
+			}
+		}
+	}
+
+};
+
+}

+ 0 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_collect_default_stdout.gold.txt


+ 0 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_collect_nest_stdout.gold.txt


+ 101 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_nest_per_file_stdout.gold.txt

@@ -0,0 +1,101 @@
+<export>
+
+    <data>
+        <info path="./simple.cpp" id="1" />
+        <file-data>
+            <regions>
+                <region>
+                    <info cursor="0" name="__global__" offset_end="639" line_begin="3" type="global" line_end="65" offset_begin="2" />
+                    <data />
+                    <subregions>
+                        <subregion>
+                            <info cursor="4" name="hmm" offset_end="638" line_begin="3" type="namespace" line_end="64" offset_begin="2" />
+                            <data />
+                            <subregions>
+                                <subregion>
+                                    <info cursor="7" name="A" offset_end="634" line_begin="7" type="class" line_end="62" offset_begin="76" />
+                                    <data />
+                                    <subregions>
+                                        <subregion>
+                                            <info cursor="10" name="A" offset_end="234" line_begin="10" type="function" line_end="21" offset_begin="88" />
+                                            <data>
+                                                <std.code.complexity cyclomatic="3">
+                                                    <__diff__ cyclomatic="0" />
+                                                </std.code.complexity>
+                                            </data>
+                                            <subregions>
+                                            </subregions>
+                                        </subregion>
+                                        <subregion>
+                                            <info cursor="23" name="func" offset_end="420" line_begin="23" type="function" line_end="39" offset_begin="237" />
+                                            <data>
+                                                <std.code.complexity cyclomatic="1">
+                                                    <__diff__ cyclomatic="0" />
+                                                </std.code.complexity>
+                                            </data>
+                                            <subregions>
+                                                <subregion>
+                                                    <info cursor="25" name="embeded" offset_end="406" line_begin="25" type="class" line_end="37" offset_begin="266" />
+                                                    <data />
+                                                    <subregions>
+                                                        <subregion>
+                                                            <info cursor="27" name="embeded" offset_end="402" line_begin="27" type="function" line_end="36" offset_begin="287" />
+                                                            <data>
+                                                                <std.code.complexity cyclomatic="2">
+                                                                    <__diff__ cyclomatic="1" />
+                                                                </std.code.complexity>
+                                                            </data>
+                                                            <subregions>
+                                                            </subregions>
+                                                        </subregion>
+                                                    </subregions>
+                                                </subregion>
+                                            </subregions>
+                                        </subregion>
+                                        <subregion>
+                                            <info cursor="41" name="never" offset_end="514" line_begin="41" type="function" line_end="48" offset_begin="423" />
+                                            <data>
+                                                <std.code.complexity cyclomatic="1">
+                                                    <__diff__ cyclomatic="0" />
+                                                </std.code.complexity>
+                                            </data>
+                                            <subregions>
+                                            </subregions>
+                                        </subregion>
+                                        <subregion>
+                                            <info cursor="50" name="new_func" offset_end="550" line_begin="50" type="function" line_end="53" offset_begin="517" />
+                                            <data>
+                                                <std.code.complexity cyclomatic="0" />
+                                            </data>
+                                            <subregions>
+                                            </subregions>
+                                        </subregion>
+                                        <subregion>
+                                            <info cursor="55" name="new_func_complex" offset_end="611" line_begin="55" type="function" line_end="59" offset_begin="553" />
+                                            <data>
+                                                <std.code.complexity cyclomatic="1" />
+                                            </data>
+                                            <subregions>
+                                            </subregions>
+                                        </subregion>
+                                    </subregions>
+                                </subregion>
+                            </subregions>
+                        </subregion>
+                    </subregions>
+                </region>
+            </regions>
+        </file-data>
+        <subfiles>
+        </subfiles>
+        <subdirs>
+        </subdirs>
+        <aggregated-data>
+            <std.code.complexity>
+                <cyclomatic max="3" total="8.0" avg="1.33333333333" min="0">
+                    <__diff__ max="0" total="2.0" avg="-0.166666666667" min="-1" />
+                </cyclomatic>
+            </std.code.complexity>
+        </aggregated-data>
+    </data>
+</export>

+ 19 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_nest_stdout.gold.txt

@@ -0,0 +1,19 @@
+<export>
+
+    <data>
+        <info path="" id="1" />
+        <file-data />
+        <subfiles>
+        </subfiles>
+        <subdirs>
+            <subdir>.</subdir>
+        </subdirs>
+        <aggregated-data>
+            <std.code.complexity>
+                <cyclomatic max="3" total="11.0" avg="1.57142857143" min="0">
+                    <__diff__ max="0" total="5.0" avg="0.0714285714286" min="-1" />
+                </cyclomatic>
+            </std.code.complexity>
+        </aggregated-data>
+    </data>
+</export>

+ 1 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_python_stdout.gold.txt

@@ -0,0 +1 @@
+{'export': [{'data: {'info': {'path': '', 'id': 1}, 'file-data': {}, 'subfiles': [], 'subdirs': [u'.'], 'aggregated-data': {'std.code.complexity': {'cyclomatic': {'total': 6.0, 'max': 3, 'avg': 1.5, 'min': 1}}}}}]}

+ 23 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_txt_stdout.gold.txt

@@ -0,0 +1,23 @@
+================================================================================
+Export
+________________________________________________________________________________
+
+--------------------------------------------------------------------------------
+data:  
+.   info: 
+.   .   path=""
+.   .   id="1"
+.   file-data: 
+.   subfiles:
+.   subdirs:
+.   .   .
+.   aggregated-data:  
+.   .   std.code.complexity:  
+.   .   .   cyclomatic: 
+.   .   .   .   max="3"
+.   .   .   .   total="6.0"
+.   .   .   .   avg="1.5"
+.   .   .   .   min="1"
+================================================================================
+
+

+ 17 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_export_format_export_xml_stdout.gold.txt

@@ -0,0 +1,17 @@
+<export>
+
+    <data>
+        <info path="" id="1" />
+        <file-data />
+        <subfiles>
+        </subfiles>
+        <subdirs>
+            <subdir>.</subdir>
+        </subdirs>
+        <aggregated-data>
+            <std.code.complexity>
+                <cyclomatic max="3" total="6.0" avg="1.5" min="1" />
+            </std.code.complexity>
+        </aggregated-data>
+    </data>
+</export>

+ 43 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_help_collect_default_stdout.gold.txt

@@ -0,0 +1,43 @@
+Usage: metrixpp.py collect [options] -- [path 1] ... [path N]
+
+Options:
+  -h, --help            show this help message and exit
+  --general.db-file=GENERAL.DB_FILE
+                        Primary database file to write (by the collector) and
+                        post-process (by other tools) [default: ./metrixpp.db]
+  --general.db-file-prev=GENERAL.DB_FILE_PREV
+                        Database file with data collected for the
+                        past/previous revision. If it is set for the collector
+                        tool to perform an incremental/iterative collection,
+                        it may reduce the processing time significantly. Post-
+                        processing tools use it in order to recognise/evaluate
+                        change trends. [default: none].
+  --general.non-recursively
+                        If the option is set (True), sub-directories are not
+                        processed [default: False]
+  --general.exclude-files=GENERAL.EXCLUDE_FILES
+                        Defines the pattern to exclude files from processing
+                        [default: ^[.]]
+  --general.proctime-on
+                        If the option is set (True), the tool measures
+                        processing time per file [default: False]
+  --general.procerrors-on
+                        If the option is set (True), the tool counts number of
+                        processing/parsing errors per file [default: False]
+  --general.log-level=GENERAL.LOG_LEVEL
+                        Defines log level. Possible values are
+                        'DEBUG','INFO','WARNING' or 'ERROR'. Default value is
+                        inherited from environment variable
+                        'METRIXPLUSPLUS_LOG_LEVEL' if set. [default: INFO]
+  --std.code.complexity.on
+                        Enables collection of complexity metrics: cyclomatic
+                        by McCabe [default: False]
+  --std.code.cpp.files=STD.CODE.CPP.FILES
+                        Enumerates filename extensions to match C/C++ files
+                        [default: *.c,*.h,*.cpp,*.hpp,*.cc,*.hh,*.cxx,*.hxx]
+  --std.code.cs.files=STD.CODE.CS.FILES
+                        Enumerates filename extensions to match C# files
+                        [default: *.cs]
+  --std.code.java.files=STD.CODE.JAVA.FILES
+                        Enumerates filename extensions to match Java files
+                        [default: *.java]

+ 32 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_help_export_default_stdout.gold.txt

@@ -0,0 +1,32 @@
+Usage: metrixpp.py export [options] -- [path 1] ... [path N]
+
+Options:
+  -h, --help            show this help message and exit
+  --general.log-level=GENERAL.LOG_LEVEL
+                        Defines log level. Possible values are
+                        'DEBUG','INFO','WARNING' or 'ERROR'. Default value is
+                        inherited from environment variable
+                        'METRIXPLUSPLUS_LOG_LEVEL' if set. [default: INFO]
+  --general.db-file=GENERAL.DB_FILE
+                        Primary database file to write (by the collector) and
+                        post-process (by other tools) [default: ./metrixpp.db]
+  --general.db-file-prev=GENERAL.DB_FILE_PREV
+                        Database file with data collected for the
+                        past/previous revision. If it is set for the collector
+                        tool to perform an incremental/iterative collection,
+                        it may reduce the processing time significantly. Post-
+                        processing tools use it in order to recognise/evaluate
+                        change trends. [default: none].
+  --general.format=GENERAL.FORMAT
+                        Format of the output data. Possible values are 'xml',
+                        'txt' or 'python' [default: xml]
+  --general.namespaces=GENERAL.NAMESPACES
+                        Allows to enumerate namespaces of interest. If not
+                        defined all namespaces available in database file will
+                        be processed. Separate several namespaces by comma,
+                        for example 'general,std.code.complexity' [default:
+                        none]
+  --general.nest-regions
+                        If the option is set (True), data for regions is
+                        exported in the form of a tree. Otherwise, all regions
+                        are exported in plain list. [default: False]

+ 19 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_help_info_default_stdout.gold.txt

@@ -0,0 +1,19 @@
+Usage: metrixpp.py info [options] -- [path 1] ... [path N]
+
+Options:
+  -h, --help            show this help message and exit
+  --general.log-level=GENERAL.LOG_LEVEL
+                        Defines log level. Possible values are
+                        'DEBUG','INFO','WARNING' or 'ERROR'. Default value is
+                        inherited from environment variable
+                        'METRIXPLUSPLUS_LOG_LEVEL' if set. [default: INFO]
+  --general.db-file=GENERAL.DB_FILE
+                        Primary database file to write (by the collector) and
+                        post-process (by other tools) [default: ./metrixpp.db]
+  --general.db-file-prev=GENERAL.DB_FILE_PREV
+                        Database file with data collected for the
+                        past/previous revision. If it is set for the collector
+                        tool to perform an incremental/iterative collection,
+                        it may reduce the processing time significantly. Post-
+                        processing tools use it in order to recognise/evaluate
+                        change trends. [default: none].

+ 41 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_help_limit_default_stdout.gold.txt

@@ -0,0 +1,41 @@
+Usage: metrixpp.py limit [options] -- [path 1] ... [path N]
+
+Options:
+  -h, --help            show this help message and exit
+  --general.log-level=GENERAL.LOG_LEVEL
+                        Defines log level. Possible values are
+                        'DEBUG','INFO','WARNING' or 'ERROR'. Default value is
+                        inherited from environment variable
+                        'METRIXPLUSPLUS_LOG_LEVEL' if set. [default: INFO]
+  --general.db-file=GENERAL.DB_FILE
+                        Primary database file to write (by the collector) and
+                        post-process (by other tools) [default: ./metrixpp.db]
+  --general.db-file-prev=GENERAL.DB_FILE_PREV
+                        Database file with data collected for the
+                        past/previous revision. If it is set for the collector
+                        tool to perform an incremental/iterative collection,
+                        it may reduce the processing time significantly. Post-
+                        processing tools use it in order to recognise/evaluate
+                        change trends. [default: none].
+  --general.warn=GENERAL.WARN
+                        Defines the warnings mode. 'new' - warnings for new
+                        regions only, 'trend' - warnings for new regions and
+                        for bad trend of modified regions, 'touched' -
+                        warnings for new regions and modified regions, 'all' -
+                        all warnings active [default: all]
+  --general.min-limit=GENERAL.MIN_LIMIT
+                        A threshold per 'namespace:field' metric in order to
+                        select regions, which have got metric value less than
+                        the specified limit. This option can be specified
+                        multiple times, if it is necessary to apply several
+                        limits. Should be in the format: <namespace>:<field
+                        >:<limit-value>, for example:
+                        'std.code.complexity:cyclomatic:7'.
+  --general.max-limit=GENERAL.MAX_LIMIT
+                        A threshold per 'namespace:field' metric in order to
+                        select regions, which have got metric value more than
+                        the specified limit. This option can be specified
+                        multiple times, if it is necessary to apply several
+                        limits. Should be in the format: <namespace>:<field
+                        >:<limit-value>, for example:
+                        'std.code.complexity:cyclomatic:7'.

+ 5 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_collect_default_stderr.gold.txt

@@ -0,0 +1,5 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Excluding: ./.unused.cpp
+[LOG]: INFO:	Skipping: ./dummy.txt
+[LOG]: INFO:	Processing: ./simple.cpp
+[LOG]: WARNING:	Exit code: 0. Time spent: 2.4 seconds. Done

+ 0 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_collect_default_stdout.gold.txt


+ 5 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_collect_second_stderr.gold.txt

@@ -0,0 +1,5 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Excluding: ./.unused.cpp
+[LOG]: INFO:	Processing: ./simple.cpp
+[LOG]: INFO:	Processing: ./simple2.cpp
+[LOG]: WARNING:	Exit code: 0. Time spent: 0.29 seconds. Done

+ 0 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_collect_second_stdout.gold.txt


+ 3 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_default_stderr.gold.txt

@@ -0,0 +1,3 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Processing: 
+[LOG]: WARNING:	Exit code: 0. Time spent: 0.14 seconds. Done

+ 17 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_default_stdout.gold.txt

@@ -0,0 +1,17 @@
+<export>
+
+    <data>
+        <info path="" id="1" />
+        <file-data />
+        <subfiles>
+        </subfiles>
+        <subdirs>
+            <subdir>.</subdir>
+        </subdirs>
+        <aggregated-data>
+            <std.code.complexity>
+                <cyclomatic max="3" total="6.0" avg="1.5" min="1" />
+            </std.code.complexity>
+        </aggregated-data>
+    </data>
+</export>

+ 3 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_second_per_file_stderr.gold.txt

@@ -0,0 +1,3 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Processing: ./simple.cpp
+[LOG]: WARNING:	Exit code: 0. Time spent: 0.15 seconds. Done

+ 81 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_second_per_file_stdout.gold.txt

@@ -0,0 +1,81 @@
+<export>
+
+    <data>
+        <info path="./simple.cpp" id="1" />
+        <file-data>
+            <regions>
+                <region>
+                    <info cursor="0" name="__global__" offset_end="639" line_begin="3" type="global" line_end="65" offset_begin="2" />
+                    <data />
+                </region>
+                <region>
+                    <info cursor="4" name="hmm" offset_end="638" line_begin="3" type="namespace" line_end="64" offset_begin="2" />
+                    <data />
+                </region>
+                <region>
+                    <info cursor="7" name="A" offset_end="634" line_begin="7" type="class" line_end="62" offset_begin="76" />
+                    <data />
+                </region>
+                <region>
+                    <info cursor="10" name="A" offset_end="234" line_begin="10" type="function" line_end="21" offset_begin="88" />
+                    <data>
+                        <std.code.complexity cyclomatic="3">
+                            <__diff__ cyclomatic="0" />
+                        </std.code.complexity>
+                    </data>
+                </region>
+                <region>
+                    <info cursor="23" name="func" offset_end="420" line_begin="23" type="function" line_end="39" offset_begin="237" />
+                    <data>
+                        <std.code.complexity cyclomatic="1">
+                            <__diff__ cyclomatic="0" />
+                        </std.code.complexity>
+                    </data>
+                </region>
+                <region>
+                    <info cursor="25" name="embeded" offset_end="406" line_begin="25" type="class" line_end="37" offset_begin="266" />
+                    <data />
+                </region>
+                <region>
+                    <info cursor="27" name="embeded" offset_end="402" line_begin="27" type="function" line_end="36" offset_begin="287" />
+                    <data>
+                        <std.code.complexity cyclomatic="2">
+                            <__diff__ cyclomatic="1" />
+                        </std.code.complexity>
+                    </data>
+                </region>
+                <region>
+                    <info cursor="41" name="never" offset_end="514" line_begin="41" type="function" line_end="48" offset_begin="423" />
+                    <data>
+                        <std.code.complexity cyclomatic="1">
+                            <__diff__ cyclomatic="0" />
+                        </std.code.complexity>
+                    </data>
+                </region>
+                <region>
+                    <info cursor="50" name="new_func" offset_end="550" line_begin="50" type="function" line_end="53" offset_begin="517" />
+                    <data>
+                        <std.code.complexity cyclomatic="0" />
+                    </data>
+                </region>
+                <region>
+                    <info cursor="55" name="new_func_complex" offset_end="611" line_begin="55" type="function" line_end="59" offset_begin="553" />
+                    <data>
+                        <std.code.complexity cyclomatic="1" />
+                    </data>
+                </region>
+            </regions>
+        </file-data>
+        <subfiles>
+        </subfiles>
+        <subdirs>
+        </subdirs>
+        <aggregated-data>
+            <std.code.complexity>
+                <cyclomatic max="3" total="8.0" avg="1.33333333333" min="0">
+                    <__diff__ max="0" total="2.0" avg="-0.166666666667" min="-1" />
+                </cyclomatic>
+            </std.code.complexity>
+        </aggregated-data>
+    </data>
+</export>

+ 3 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_second_stderr.gold.txt

@@ -0,0 +1,3 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Processing: 
+[LOG]: WARNING:	Exit code: 0. Time spent: 0.15 seconds. Done

+ 19 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_export_second_stdout.gold.txt

@@ -0,0 +1,19 @@
+<export>
+
+    <data>
+        <info path="" id="1" />
+        <file-data />
+        <subfiles>
+        </subfiles>
+        <subdirs>
+            <subdir>.</subdir>
+        </subdirs>
+        <aggregated-data>
+            <std.code.complexity>
+                <cyclomatic max="3" total="11.0" avg="1.57142857143" min="0">
+                    <__diff__ max="0" total="5.0" avg="0.0714285714286" min="-1" />
+                </cyclomatic>
+            </std.code.complexity>
+        </aggregated-data>
+    </data>
+</export>

+ 2 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_info_default_stderr.gold.txt

@@ -0,0 +1,2 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: WARNING:	Exit code: 0. Time spent: 0.1 seconds. Done

+ 16 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_info_default_stdout.gold.txt

@@ -0,0 +1,16 @@
+Properties:
+	version	=>	1.0
+	core.log:version	=>	1.0
+	std.code.complexity:version	=>	1.1
+	std.code.cpp:version	=>	1.1
+	std.code.cpp:files	=>	*.c,*.cc,*.cpp,*.cxx,*.h,*.hh,*.hpp,*.hxx
+	std.code.cs:version	=>	1.0
+	std.code.cs:files	=>	*.cs
+	std.code.java:version	=>	1.1
+	std.code.java:files	=>	*.java
+Namespaces:
+	std.code.complexity
+		- cyclomatic
+	general
+Files:
+	./simple.cpp

+ 2 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_info_second_stderr.gold.txt

@@ -0,0 +1,2 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: WARNING:	Exit code: 0. Time spent: 0.11 seconds. Done

+ 17 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_info_second_stdout.gold.txt

@@ -0,0 +1,17 @@
+Properties:
+	version	=>	1.0
+	core.log:version	=>	1.0
+	std.code.complexity:version	=>	1.1
+	std.code.cpp:version	=>	1.1
+	std.code.cpp:files	=>	*.c,*.cc,*.cpp,*.cxx,*.h,*.hh,*.hpp,*.hxx
+	std.code.cs:version	=>	1.0
+	std.code.cs:files	=>	*.cs
+	std.code.java:version	=>	1.1
+	std.code.java:files	=>	*.java
+Namespaces:
+	std.code.complexity
+		- cyclomatic
+	general
+Files:
+(!) 	./simple.cpp [modified]
+(!) 	./simple2.cpp [new]

+ 4 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_default_stderr.gold.txt

@@ -0,0 +1,4 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Processing: 
+[LOG]: INFO:	Applying limit: namespace 'std.code.complexity', filter '('cyclomatic', '>', 0.0)'
+[LOG]: WARNING:	Exit code: 4. Time spent: 0.14 seconds. Done

+ 32 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_default_stdout.gold.txt

@@ -0,0 +1,32 @@
+./simple.cpp:10: warning: Metric 'std.code.complexity/cyclomatic' for region 'A' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : A
+	Metric value   : 3
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+
+./simple.cpp:23: warning: Metric 'std.code.complexity/cyclomatic' for region 'func' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : func
+	Metric value   : 1
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+
+./simple.cpp:27: warning: Metric 'std.code.complexity/cyclomatic' for region 'embeded' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : embeded
+	Metric value   : 1
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+
+./simple.cpp:39: warning: Metric 'std.code.complexity/cyclomatic' for region 'never' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : never
+	Metric value   : 1
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+

+ 4 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_stderr.gold.txt

@@ -0,0 +1,4 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Processing: 
+[LOG]: INFO:	Applying limit: namespace 'std.code.complexity', filter '('cyclomatic', '>', 0.0)'
+[LOG]: WARNING:	Exit code: 6. Time spent: 0.14 seconds. Done

+ 48 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_stdout.gold.txt

@@ -0,0 +1,48 @@
+./simple.cpp:10: warning: Metric 'std.code.complexity/cyclomatic' for region 'A' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : A
+	Metric value   : 3
+	Modified       : False
+	Change trend   : 0
+	Limit          : 0.0
+
+./simple.cpp:23: warning: Metric 'std.code.complexity/cyclomatic' for region 'func' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : func
+	Metric value   : 1
+	Modified       : False
+	Change trend   : 0
+	Limit          : 0.0
+
+./simple.cpp:27: warning: Metric 'std.code.complexity/cyclomatic' for region 'embeded' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : embeded
+	Metric value   : 2
+	Modified       : True
+	Change trend   : +1
+	Limit          : 0.0
+
+./simple.cpp:41: warning: Metric 'std.code.complexity/cyclomatic' for region 'never' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : never
+	Metric value   : 1
+	Modified       : True
+	Change trend   : 0
+	Limit          : 0.0
+
+./simple.cpp:55: warning: Metric 'std.code.complexity/cyclomatic' for region 'new_func_complex' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : new_func_complex
+	Metric value   : 1
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+
+./simple2.cpp:10: warning: Metric 'std.code.complexity/cyclomatic' for region 'New_A' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : New_A
+	Metric value   : 3
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+

+ 4 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_all_stderr.gold.txt

@@ -0,0 +1,4 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Processing: 
+[LOG]: INFO:	Applying limit: namespace 'std.code.complexity', filter '('cyclomatic', '>', 0.0)'
+[LOG]: WARNING:	Exit code: 6. Time spent: 0.14 seconds. Done

+ 48 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_all_stdout.gold.txt

@@ -0,0 +1,48 @@
+./simple.cpp:10: warning: Metric 'std.code.complexity/cyclomatic' for region 'A' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : A
+	Metric value   : 3
+	Modified       : False
+	Change trend   : 0
+	Limit          : 0.0
+
+./simple.cpp:23: warning: Metric 'std.code.complexity/cyclomatic' for region 'func' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : func
+	Metric value   : 1
+	Modified       : False
+	Change trend   : 0
+	Limit          : 0.0
+
+./simple.cpp:27: warning: Metric 'std.code.complexity/cyclomatic' for region 'embeded' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : embeded
+	Metric value   : 2
+	Modified       : True
+	Change trend   : +1
+	Limit          : 0.0
+
+./simple.cpp:41: warning: Metric 'std.code.complexity/cyclomatic' for region 'never' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : never
+	Metric value   : 1
+	Modified       : True
+	Change trend   : 0
+	Limit          : 0.0
+
+./simple.cpp:55: warning: Metric 'std.code.complexity/cyclomatic' for region 'new_func_complex' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : new_func_complex
+	Metric value   : 1
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+
+./simple2.cpp:10: warning: Metric 'std.code.complexity/cyclomatic' for region 'New_A' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : New_A
+	Metric value   : 3
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+

+ 5 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_new_stderr.gold.txt

@@ -0,0 +1,5 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Identifying changed files...
+[LOG]: INFO:	Processing: 
+[LOG]: INFO:	Applying limit: namespace 'std.code.complexity', filter '('cyclomatic', '>', 0.0)'
+[LOG]: WARNING:	Exit code: 2. Time spent: 0.15 seconds. Done

+ 16 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_new_stdout.gold.txt

@@ -0,0 +1,16 @@
+./simple.cpp:55: warning: Metric 'std.code.complexity/cyclomatic' for region 'new_func_complex' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : new_func_complex
+	Metric value   : 1
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+
+./simple2.cpp:10: warning: Metric 'std.code.complexity/cyclomatic' for region 'New_A' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : New_A
+	Metric value   : 3
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+

+ 5 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_touched_stderr.gold.txt

@@ -0,0 +1,5 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Identifying changed files...
+[LOG]: INFO:	Processing: 
+[LOG]: INFO:	Applying limit: namespace 'std.code.complexity', filter '('cyclomatic', '>', 0.0)'
+[LOG]: WARNING:	Exit code: 4. Time spent: 0.14 seconds. Done

+ 32 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_touched_stdout.gold.txt

@@ -0,0 +1,32 @@
+./simple.cpp:27: warning: Metric 'std.code.complexity/cyclomatic' for region 'embeded' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : embeded
+	Metric value   : 2
+	Modified       : True
+	Change trend   : +1
+	Limit          : 0.0
+
+./simple.cpp:41: warning: Metric 'std.code.complexity/cyclomatic' for region 'never' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : never
+	Metric value   : 1
+	Modified       : True
+	Change trend   : 0
+	Limit          : 0.0
+
+./simple.cpp:55: warning: Metric 'std.code.complexity/cyclomatic' for region 'new_func_complex' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : new_func_complex
+	Metric value   : 1
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+
+./simple2.cpp:10: warning: Metric 'std.code.complexity/cyclomatic' for region 'New_A' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : New_A
+	Metric value   : 3
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+

+ 5 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_trend_stderr.gold.txt

@@ -0,0 +1,5 @@
+[LOG]: WARNING:	Logging enabled with INFO level
+[LOG]: INFO:	Identifying changed files...
+[LOG]: INFO:	Processing: 
+[LOG]: INFO:	Applying limit: namespace 'std.code.complexity', filter '('cyclomatic', '>', 0.0)'
+[LOG]: WARNING:	Exit code: 3. Time spent: 0.14 seconds. Done

+ 24 - 0
releases/metrixplusplus-1.1.70/tests/general/test_basic/test_workflow_limit_second_warn_trend_stdout.gold.txt

@@ -0,0 +1,24 @@
+./simple.cpp:27: warning: Metric 'std.code.complexity/cyclomatic' for region 'embeded' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : embeded
+	Metric value   : 2
+	Modified       : True
+	Change trend   : +1
+	Limit          : 0.0
+
+./simple.cpp:55: warning: Metric 'std.code.complexity/cyclomatic' for region 'new_func_complex' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : new_func_complex
+	Metric value   : 1
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+
+./simple2.cpp:10: warning: Metric 'std.code.complexity/cyclomatic' for region 'New_A' exceeds the limit.
+	Metric name    : std.code.complexity/cyclomatic
+	Region name    : New_A
+	Metric value   : 3
+	Modified       : None
+	Change trend   : None
+	Limit          : 0.0
+

+ 46 - 0
releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp.py

@@ -0,0 +1,46 @@
+#
+#    Metrix++, Copyright 2009-2013, Metrix++ Project
+#    Link: http://metrixplusplus.sourceforge.net
+#    
+#    This file is a part of Metrix++ Tool.
+#    
+#    Metrix++ is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation, version 3 of the License.
+#    
+#    Metrix++ is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+#    GNU General Public License for more details.
+#    
+#    You should have received a copy of the GNU General Public License
+#    along with Metrix++.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+
+import unittest
+import os
+
+import tests.common
+
+class Test(tests.common.TestCase):
+
+    def test_parser(self):
+        
+        runner = tests.common.ToolRunner('collect', ['--std.code.complexity.on'])
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('export')
+        self.assertExec(runner.run())
+        
+        dirs_list = [os.path.join('.', each) for each in os.listdir(self.get_content_paths().cwd)]
+        runner = tests.common.ToolRunner('export', opts_list=['--general.format=txt'], dirs_list=dirs_list, prefix='files')
+        self.assertExec(runner.run())
+
+        runner = tests.common.ToolRunner('limit',
+                                         ['--general.max-limit=std.code.complexity:cyclomatic:0'],
+                                         exit_code=12)
+        self.assertExec(runner.run())
+
+if __name__ == '__main__':
+    unittest.main()

+ 19 - 0
releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/function_ends_on_class.cpp

@@ -0,0 +1,19 @@
+
+namespace my_namespace {
+
+class my_class {
+	int function_end_on_class()
+	{
+
+	}
+	int function_end_on_struct()
+	{
+
+	}
+	int function_end_on_namespace()
+	{
+
+	}
+};
+
+}

+ 85 - 0
releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/operator_test.hpp

@@ -0,0 +1,85 @@
+
+namespace {
+/*!
+       *  Finds the optimal cycle ratio of the policy graph
+       */
+      float_t policy_mcr()
+      {
+        std::fill(m_col_bfs.begin(), m_col_bfs.end(), my_white);
+        color_map_t vcm_ = color_map_t(m_col_bfs.begin(), m_vim);
+        typename graph_traits<Graph>::vertex_iterator uv_itr, vie;
+        boost::tie(uv_itr, vie) = vertices(m_g);
+        float_t mcr = m_bound;
+        while ( (uv_itr = std::find_if(uv_itr, vie,
+                                       boost::bind(std::equal_to<my_color_type>(),
+                                                   my_white,
+                                                   boost::bind(&color_map_t::operator[], vcm_, _1)
+                                                   )
+                                       )
+                 ) != vie )
+          ///While there are undiscovered vertices
+          {
+            vertex_t gv = find_cycle_vertex(*uv_itr);
+            float_t cr = cycle_ratio(gv) ;
+            mcr_bfv(gv, cr, vcm_);
+            if ( m_cmp(mcr, cr) )  mcr = cr;
+            ++uv_itr;
+          }
+        return mcr;
+      }
+
+      function_after(){}
+}
+
+
+class ALL_operators {
+	operator int () {}
+	operator new () {}
+	operator delete () {}
+	operator new[ ] () {}
+	operator delete [] () {}
+	operator+ () {}
+	operator - () {}
+	operator * () {}
+	operator / () {}
+	operator= () {}
+	operator< () {}
+	operator > () {}
+	operator+= () {}
+	operator -= () {}
+	operator*= () {}
+	operator/= () {}
+	operator<<() {}
+	operator >> () {}
+	operator <<=() {}
+	operator>>=() {}
+	operator==() {}
+	operator!=() {}
+	operator<=() {}
+	operator >= () {}
+	operator ++ () {}
+	operator--() {}
+	operator%() {}
+	operator &() {}
+	operator ^() {}
+	operator !() {}
+	operator|() {}
+	operator~() {}
+	operator &=() {}
+	operator ^=() {}
+	operator |=() {}
+	operator &&() {}
+	operator ||  () {}
+	operator %=() {}
+	operator []  () {}
+	operator  ()  () {}
+	operator() () {}
+	operator()(){}
+	operator()   (){}
+	operator ()() {}
+	operator ()(){}
+	operator ,() {}
+	operator ->*() {}
+	operator->() {}
+	operator.() {}
+};

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 0
releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/test.c


+ 52 - 0
releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/test2.cpp

@@ -0,0 +1,52 @@
+#ifdef preprocessor___
+
+   	   #endif
+
+class A
+{
+	A() : m_asign(0), asd(4)
+	{
+if(){}
+	}int method()
+	{
+		return 0;
+	}int operator ++ ()
+	{
+		return 0;
+	}
+
+	int m_asign;
+};
+
+//
+namespace aaa {
+int main(int a, string b)
+{
+	return 0;
+
+	try {
+
+	}
+	catch (a) // for cyclomatic complexity test
+	{
+
+	}
+	catch (b) // for cyclomatic complexity test
+	{
+
+	}
+}
+}
+
+"text" // C++ comments
+"text________________"
+"text" /* C comment */
+"text________________"
+"multiline____________
+_text________________"
+"multiline____________
+_text________________"
+"text" /* C multiline_
+_____comment */ "text"
+"text" /* C multiline_
+_____comment */ "text"

+ 0 - 0
releases/metrixplusplus-1.1.70/tests/general/test_std_code_cpp/sources/test3.cpp


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.