warn.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. #
  2. # Metrix++, Copyright 2009-2013, Metrix++ Project
  3. # Link: http://metrixplusplus.sourceforge.net
  4. #
  5. # This file is a part of Metrix++ Tool.
  6. #
  7. # Metrix++ is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, version 3 of the License.
  10. #
  11. # Metrix++ is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with Metrix++. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. import re
  20. import mpp.api
  21. class Plugin(mpp.api.Plugin, mpp.api.IConfigurable):
  22. MODE_NEW = 0x01
  23. MODE_TREND = 0x03
  24. MODE_TOUCHED = 0x07
  25. MODE_ALL = 0x15
  26. def declare_configuration(self, parser):
  27. self.parser = parser
  28. parser.add_option("--warn-mode", "--wm", default='all', choices=['new', 'trend', 'touched', 'all'],
  29. help="Defines the warnings mode. "
  30. "'new' - warnings for new regions only, "
  31. "'trend' - warnings for new regions and for bad trend of modified regions, "
  32. "'touched' - warnings for new regions and modified regions, "
  33. "'all' - all warnings active "
  34. "[default: %default]")
  35. parser.add_option("--min-limit", "--min", action="multiopt",
  36. help="A threshold per 'namespace:field' metric in order to select regions, "
  37. "which have got metric value less than the specified limit. "
  38. "This option can be specified multiple times, if it is necessary to apply several limits. "
  39. "Should be in the format: <namespace>:<field>:<limit-value>, for example: "
  40. "'std.code.lines:comments:1'.")
  41. parser.add_option("--max-limit", "--max", action="multiopt",
  42. help="A threshold per 'namespace:field' metric in order to select regions, "
  43. "which have got metric value more than the specified limit. "
  44. "This option can be specified multiple times, if it is necessary to apply several limits. "
  45. "Should be in the format: <namespace>:<field>:<limit-value>, for example: "
  46. "'std.code.complexity:cyclomatic:7'.")
  47. def configure(self, options):
  48. if options.__dict__['warn_mode'] == 'new':
  49. self.mode = self.MODE_NEW
  50. elif options.__dict__['warn_mode'] == 'trend':
  51. self.mode = self.MODE_TREND
  52. elif options.__dict__['warn_mode'] == 'touched':
  53. self.mode = self.MODE_TOUCHED
  54. elif options.__dict__['warn_mode'] == 'all':
  55. self.mode = self.MODE_ALL
  56. if self.mode != self.MODE_ALL and options.__dict__['db_file_prev'] == None:
  57. self.parser.error("The mode '" + options.__dict__['warn_mode'] + "' for 'general.warn' option requires '--db-file-prev' option set")
  58. class Limit(object):
  59. def __init__(self, limit_type, limit, namespace, field, db_filter):
  60. self.type = limit_type
  61. self.limit = limit
  62. self.namespace = namespace
  63. self.field = field
  64. self.filter = db_filter
  65. def __repr__(self):
  66. return "namespace '" + self.namespace + "', filter '" + str(self.filter) + "'"
  67. self.limits = []
  68. pattern = re.compile(r'''([^:]+)[:]([^:]+)[:]([-+]?[0-9]+(?:[.][0-9]+)?)''')
  69. if options.__dict__['max_limit'] != None:
  70. for each in options.__dict__['max_limit']:
  71. match = re.match(pattern, each)
  72. if match == None:
  73. self.parser.error("Invalid format of the '--max-limit' option: " + each)
  74. limit = Limit("max", float(match.group(3)), match.group(1), match.group(2), (match.group(2), '>', float(match.group(3))))
  75. self.limits.append(limit)
  76. if options.__dict__['min_limit'] != None:
  77. for each in options.__dict__['min_limit']:
  78. match = re.match(pattern, each)
  79. if match == None:
  80. self.parser.error("Invalid format of the '--min-limit' option: " + each)
  81. limit = Limit("min", float(match.group(3)), match.group(1), match.group(2), (match.group(2), '<', float(match.group(3))))
  82. self.limits.append(limit)
  83. def initialize(self):
  84. super(Plugin, self).initialize()
  85. db_loader = self.get_plugin_loader().get_plugin('mpp.dbf').get_loader()
  86. self._verify_namespaces(db_loader.iterate_namespace_names())
  87. for each in db_loader.iterate_namespace_names():
  88. self._verify_fields(each, db_loader.get_namespace(each).iterate_field_names())
  89. def _verify_namespaces(self, valid_namespaces):
  90. valid = []
  91. for each in valid_namespaces:
  92. valid.append(each)
  93. for each in self.limits:
  94. if each.namespace not in valid:
  95. self.parser.error("Invalid limit option (namespace does not exist): " + each.namespace)
  96. def _verify_fields(self, namespace, valid_fields):
  97. valid = []
  98. for each in valid_fields:
  99. valid.append(each)
  100. for each in self.limits:
  101. if each.namespace == namespace:
  102. if each.field not in valid:
  103. self.parser.error("Invalid limit option (field does not exist): " + each.namespace + ":" + each.field)
  104. def iterate_limits(self):
  105. for each in self.limits:
  106. yield each
  107. def is_mode_matched(self, limit, value, diff, is_modified):
  108. if is_modified == None:
  109. return True
  110. if self.mode == self.MODE_ALL:
  111. return True
  112. if self.mode == self.MODE_TOUCHED and is_modified == True:
  113. return True
  114. if self.mode == self.MODE_TREND and is_modified == True:
  115. if limit < value and diff > 0:
  116. return True
  117. if limit > value and diff < 0:
  118. return True
  119. return False