# frozen_string_literal: true require 'nokogiri' require 'set' require_relative '../cmd' require_relative '../resource_locator' module PmdTester # This class is responsible for generation dynamic configuration # according to the difference between base and patch branch of Pmd. # Attention: we only consider java rulesets now. class RuleSetBuilder include PmdTester ALL_RULE_SETS = Set['bestpractices', 'codestyle', 'design', 'documentation', 'errorprone', 'multithreading', 'performance', 'security'].freeze PATH_TO_PMD_JAVA_BASED_RULES = 'pmd-java/src/main/java/net/sourceforge/pmd/lang/java/rule' PATH_TO_PMD_XPATH_BASED_RULES = 'pmd-java/src/main/resources/category/java' PATH_TO_ALL_JAVA_RULES = ResourceLocator.locate('config/all-java.xml') PATH_TO_DYNAMIC_CONFIG = 'target/dynamic-config.xml' NO_JAVA_RULES_CHANGED_MESSAGE = 'No java rules have been changed!' def initialize(options) @options = options end def build filenames = diff_filenames rule_sets = get_rule_sets(filenames) output_filter_set(rule_sets) build_config_file(rule_sets) logger.debug "Dynamic configuration: #{[rule_sets]}" end def output_filter_set(rule_sets) if rule_sets == ALL_RULE_SETS # if `rule_sets` contains all rule sets, than no need to filter the baseline @options.filter_set = nil else @options.filter_set = rule_sets end end def diff_filenames filenames = nil Dir.chdir(@options.local_git_repo) do base = @options.base_branch patch = @options.patch_branch # We only need to support git here, since PMD's repo is using git. diff_cmd = "git diff --name-only #{base}..#{patch} -- pmd/core pmd/java" filenames = Cmd.execute(diff_cmd) end filenames.split("\n") end def get_rule_sets(filenames) rule_sets = Set[] filenames.each do |filename| match_data = %r{#{PATH_TO_PMD_JAVA_BASED_RULES}/([^/]+)/[^/]+Rule.java}.match(filename) if match_data.nil? match_data = %r{#{PATH_TO_PMD_XPATH_BASED_RULES}/([^/]+).xml}.match(filename) end category = if match_data.nil? nil else match_data[1] end if category.nil? rule_sets = ALL_RULE_SETS break else rule_sets.add(category) end end rule_sets end def build_config_file(rule_sets) if rule_sets.empty? puts NO_JAVA_RULES_CHANGED_MESSAGE exit 0 end doc = Nokogiri::XML(File.read(PATH_TO_ALL_JAVA_RULES)) doc.search('rule').each do |rule| rule.remove unless match_ref?(rule, rule_sets) end description = doc.at_css('description') description.content = 'The ruleset generated by PmdTester dynamically' write_dynamic_file(doc) end def match_ref?(rule_node, rule_sets) rule_sets.each do |rule_set| return true unless rule_node['ref'].index(rule_set).nil? end false end def write_dynamic_file(doc) File.open(PATH_TO_DYNAMIC_CONFIG, 'w') do |x| x << doc.to_s.gsub(/\n\s+\n/, "\n") end @options.base_config = PATH_TO_DYNAMIC_CONFIG @options.patch_config = PATH_TO_DYNAMIC_CONFIG end end end