You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

RuleActivator.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program 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 GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.qualityprofile;
  21. import com.google.common.base.Splitter;
  22. import java.util.ArrayList;
  23. import java.util.Collection;
  24. import java.util.Date;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Set;
  28. import java.util.stream.Stream;
  29. import javax.annotation.CheckForNull;
  30. import javax.annotation.Nullable;
  31. import org.apache.commons.lang.StringUtils;
  32. import org.sonar.api.rule.RuleStatus;
  33. import org.sonar.api.server.ServerSide;
  34. import org.sonar.api.server.rule.RuleParamType;
  35. import org.sonar.api.utils.System2;
  36. import org.sonar.core.util.stream.MoreCollectors;
  37. import org.sonar.db.DbClient;
  38. import org.sonar.db.DbSession;
  39. import org.sonar.db.qualityprofile.ActiveRuleDao;
  40. import org.sonar.db.qualityprofile.ActiveRuleDto;
  41. import org.sonar.db.qualityprofile.ActiveRuleKey;
  42. import org.sonar.db.qualityprofile.ActiveRuleParamDto;
  43. import org.sonar.db.qualityprofile.OrgQProfileDto;
  44. import org.sonar.db.qualityprofile.QProfileDto;
  45. import org.sonar.db.qualityprofile.RulesProfileDto;
  46. import org.sonar.db.rule.RuleDefinitionDto;
  47. import org.sonar.db.rule.RuleParamDto;
  48. import org.sonar.server.qualityprofile.RuleActivationContext.ActiveRuleWrapper;
  49. import org.sonar.server.qualityprofile.RuleActivationContext.RuleWrapper;
  50. import org.sonar.server.user.UserSession;
  51. import org.sonar.server.util.TypeValidations;
  52. import static com.google.common.base.Preconditions.checkArgument;
  53. import static org.sonar.server.exceptions.BadRequestException.checkRequest;
  54. /**
  55. * Activation and deactivation of rules in Quality profiles
  56. */
  57. @ServerSide
  58. public class RuleActivator {
  59. private final System2 system2;
  60. private final DbClient db;
  61. private final TypeValidations typeValidations;
  62. private final UserSession userSession;
  63. public RuleActivator(System2 system2, DbClient db, TypeValidations typeValidations, UserSession userSession) {
  64. this.system2 = system2;
  65. this.db = db;
  66. this.typeValidations = typeValidations;
  67. this.userSession = userSession;
  68. }
  69. public List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation, RuleActivationContext context) {
  70. context.reset(activation.getRuleUuid());
  71. return doActivate(dbSession, activation, context);
  72. }
  73. private List<ActiveRuleChange> doActivate(DbSession dbSession, RuleActivation activation, RuleActivationContext context) {
  74. RuleDefinitionDto rule = context.getRule().get();
  75. checkRequest(RuleStatus.REMOVED != rule.getStatus(), "Rule was removed: %s", rule.getKey());
  76. checkRequest(!rule.isTemplate(), "Rule template can't be activated on a Quality profile: %s", rule.getKey());
  77. List<ActiveRuleChange> changes = new ArrayList<>();
  78. ActiveRuleChange change;
  79. boolean stopCascading = false;
  80. ActiveRuleWrapper activeRule = context.getActiveRule();
  81. ActiveRuleKey activeRuleKey = ActiveRuleKey.of(context.getRulesProfile(), rule.getKey());
  82. if (activeRule == null) {
  83. if (activation.isReset()) {
  84. // ignore reset when rule is not activated
  85. return changes;
  86. }
  87. // new activation
  88. change = new ActiveRuleChange(ActiveRuleChange.Type.ACTIVATED, activeRuleKey, rule);
  89. applySeverityAndParamToChange(activation, context, change);
  90. if (context.isCascading() || isSameAsParent(change, context)) {
  91. change.setInheritance(ActiveRuleInheritance.INHERITED);
  92. }
  93. } else {
  94. // already activated
  95. if (context.isCascading() && activeRule.get().doesOverride()) {
  96. // propagating to descendants, but child profile already overrides rule -> stop propagation
  97. return changes;
  98. }
  99. change = new ActiveRuleChange(ActiveRuleChange.Type.UPDATED, activeRuleKey, rule);
  100. if (context.isCascading() && activeRule.get().getInheritance() == null) {
  101. // activate on child, then on parent -> mark child as overriding parent
  102. change.setInheritance(ActiveRuleInheritance.OVERRIDES);
  103. change.setSeverity(activeRule.get().getSeverityString());
  104. for (ActiveRuleParamDto activeParam : activeRule.getParams()) {
  105. change.setParameter(activeParam.getKey(), activeParam.getValue());
  106. }
  107. stopCascading = true;
  108. } else {
  109. applySeverityAndParamToChange(activation, context, change);
  110. if (!context.isCascading() && context.getParentActiveRule() != null) {
  111. // override rule which is already declared on parents
  112. change.setInheritance(isSameAsParent(change, context) ? ActiveRuleInheritance.INHERITED : ActiveRuleInheritance.OVERRIDES);
  113. }
  114. }
  115. if (isSame(change, activeRule)) {
  116. change = null;
  117. stopCascading = true;
  118. }
  119. }
  120. if (change != null) {
  121. changes.add(change);
  122. persist(change, context, dbSession);
  123. }
  124. if (!changes.isEmpty()) {
  125. updateProfileDates(dbSession, context);
  126. }
  127. if (!stopCascading) {
  128. changes.addAll(propagateActivationToDescendants(dbSession, activation, context));
  129. }
  130. return changes;
  131. }
  132. private void updateProfileDates(DbSession dbSession, RuleActivationContext context) {
  133. RulesProfileDto ruleProfile = context.getRulesProfile();
  134. ruleProfile.setRulesUpdatedAtAsDate(new Date(context.getDate()));
  135. db.qualityProfileDao().update(dbSession, ruleProfile);
  136. if (userSession.isLoggedIn()) {
  137. context.getProfiles().forEach(p -> db.qualityProfileDao().update(dbSession, OrgQProfileDto.from(p).setUserUpdatedAt(context.getDate())));
  138. }
  139. }
  140. /**
  141. * Update severity and params
  142. */
  143. private void applySeverityAndParamToChange(RuleActivation request, RuleActivationContext context, ActiveRuleChange change) {
  144. RuleWrapper rule = context.getRule();
  145. ActiveRuleWrapper activeRule = context.getActiveRule();
  146. ActiveRuleWrapper parentActiveRule = context.getParentActiveRule();
  147. if (request.isReset()) {
  148. applySeverityAndParamsWhenResetRequested(change, rule, parentActiveRule);
  149. } else if (context.getRulesProfile().isBuiltIn()) {
  150. applySeverityAndParamsWhenBuiltInProfile(request, context, change, rule);
  151. } else {
  152. applySeverityAndParamsWhenNonBuiltInProfile(request, context, change, rule, activeRule, parentActiveRule);
  153. }
  154. }
  155. private void applySeverityAndParamsWhenResetRequested(ActiveRuleChange change, RuleWrapper rule, @Nullable ActiveRuleWrapper parentActiveRule) {
  156. String severity = firstNonNull(
  157. parentActiveRule != null ? parentActiveRule.get().getSeverityString() : null,
  158. rule.get().getSeverityString());
  159. change.setSeverity(severity);
  160. for (RuleParamDto ruleParamDto : rule.getParams()) {
  161. String paramKey = ruleParamDto.getName();
  162. // load params from parent profile, else from default values
  163. String paramValue = firstNonNull(
  164. parentActiveRule != null ? parentActiveRule.getParamValue(paramKey) : null,
  165. rule.getParamDefaultValue(paramKey));
  166. change.setParameter(paramKey, validateParam(ruleParamDto, paramValue));
  167. }
  168. }
  169. private void applySeverityAndParamsWhenBuiltInProfile(RuleActivation request, RuleActivationContext context, ActiveRuleChange change,
  170. RuleWrapper rule) {
  171. // for builtin quality profiles, the severity from profile, when null use the default severity of the rule
  172. String severity = firstNonNull(request.getSeverity(), rule.get().getSeverityString());
  173. change.setSeverity(severity);
  174. for (RuleParamDto ruleParamDto : rule.getParams()) {
  175. String paramKey = ruleParamDto.getName();
  176. // use the value defined in the profile definition, else the rule default value
  177. String paramValue = firstNonNull(
  178. context.getRequestedParamValue(request, paramKey),
  179. rule.getParamDefaultValue(paramKey));
  180. change.setParameter(paramKey, validateParam(ruleParamDto, paramValue));
  181. }
  182. }
  183. /**
  184. * 1. apply requested severity and param
  185. * 2. if rule activated and overridden - apply user value
  186. * 3. apply parent value
  187. * 4. apply defaults
  188. */
  189. private void applySeverityAndParamsWhenNonBuiltInProfile(RuleActivation request, RuleActivationContext context, ActiveRuleChange change,
  190. RuleWrapper rule, @Nullable ActiveRuleWrapper activeRule, @Nullable ActiveRuleWrapper parentActiveRule) {
  191. String severity = getSeverityForNonBuiltInProfile(request, rule, activeRule, parentActiveRule);
  192. change.setSeverity(severity);
  193. for (RuleParamDto ruleParamDto : rule.getParams()) {
  194. String paramKey = ruleParamDto.getName();
  195. String parentValue = parentActiveRule != null ? parentActiveRule.getParamValue(paramKey) : null;
  196. String paramValue;
  197. if (context.hasRequestedParamValue(request, paramKey)) {
  198. // If the request contains the parameter then we're using either value from request, or parent value, or default value
  199. paramValue = firstNonNull(
  200. context.getRequestedParamValue(request, paramKey),
  201. parentValue,
  202. rule.getParamDefaultValue(paramKey));
  203. } else if (activeRule != null) {
  204. // If the request doesn't contain the parameter, then we're using either user value from db, or parent value if rule inherited, or default
  205. // value
  206. paramValue = firstNonNull(
  207. activeRule.get().doesOverride() ? activeRule.getParamValue(paramKey) : null,
  208. parentValue == null ? activeRule.getParamValue(paramKey) : parentValue,
  209. rule.getParamDefaultValue(paramKey));
  210. } else {
  211. paramValue = firstNonNull(
  212. parentValue,
  213. rule.getParamDefaultValue(paramKey));
  214. }
  215. change.setParameter(paramKey, validateParam(ruleParamDto, paramValue));
  216. }
  217. }
  218. private static String getSeverityForNonBuiltInProfile(RuleActivation request, RuleWrapper rule, @Nullable ActiveRuleWrapper activeRule,
  219. @Nullable ActiveRuleWrapper parentActiveRule) {
  220. String severity;
  221. if (activeRule != null) {
  222. ActiveRuleDto activeRuleDto = activeRule.get();
  223. // load severity from request, else keep existing one (if overridden), else from parent if rule inherited, else from default
  224. severity = firstNonNull(
  225. request.getSeverity(),
  226. activeRuleDto.doesOverride() ? activeRuleDto.getSeverityString() : null,
  227. parentActiveRule != null ? parentActiveRule.get().getSeverityString() : activeRuleDto.getSeverityString(),
  228. rule.get().getSeverityString());
  229. } else {
  230. // load severity from request, else from parent, else from default
  231. severity = firstNonNull(
  232. request.getSeverity(),
  233. parentActiveRule != null ? parentActiveRule.get().getSeverityString() : null,
  234. rule.get().getSeverityString());
  235. }
  236. return severity;
  237. }
  238. private List<ActiveRuleChange> propagateActivationToDescendants(DbSession dbSession, RuleActivation activation, RuleActivationContext context) {
  239. List<ActiveRuleChange> changes = new ArrayList<>();
  240. // get all inherited profiles
  241. context.getChildProfiles().forEach(child -> {
  242. context.selectChild(child);
  243. changes.addAll(doActivate(dbSession, activation, context));
  244. });
  245. return changes;
  246. }
  247. private void persist(ActiveRuleChange change, RuleActivationContext context, DbSession dbSession) {
  248. ActiveRuleDto activeRule = null;
  249. if (change.getType() == ActiveRuleChange.Type.ACTIVATED) {
  250. activeRule = doInsert(change, context, dbSession);
  251. } else if (change.getType() == ActiveRuleChange.Type.DEACTIVATED) {
  252. ActiveRuleDao dao = db.activeRuleDao();
  253. activeRule = dao.delete(dbSession, change.getKey()).orElse(null);
  254. } else if (change.getType() == ActiveRuleChange.Type.UPDATED) {
  255. activeRule = doUpdate(change, context, dbSession);
  256. }
  257. change.setActiveRule(activeRule);
  258. db.qProfileChangeDao().insert(dbSession, change.toDto(userSession.getUuid()));
  259. }
  260. private ActiveRuleDto doInsert(ActiveRuleChange change, RuleActivationContext context, DbSession dbSession) {
  261. ActiveRuleDao dao = db.activeRuleDao();
  262. RuleWrapper rule = context.getRule();
  263. ActiveRuleDto activeRule = new ActiveRuleDto();
  264. activeRule.setProfileUuid(context.getRulesProfile().getUuid());
  265. activeRule.setRuleUuid(rule.get().getUuid());
  266. activeRule.setKey(ActiveRuleKey.of(context.getRulesProfile(), rule.get().getKey()));
  267. String severity = change.getSeverity();
  268. if (severity != null) {
  269. activeRule.setSeverity(severity);
  270. }
  271. ActiveRuleInheritance inheritance = change.getInheritance();
  272. if (inheritance != null) {
  273. activeRule.setInheritance(inheritance.name());
  274. }
  275. activeRule.setUpdatedAt(system2.now());
  276. activeRule.setCreatedAt(system2.now());
  277. dao.insert(dbSession, activeRule);
  278. for (Map.Entry<String, String> param : change.getParameters().entrySet()) {
  279. if (param.getValue() != null) {
  280. ActiveRuleParamDto paramDto = ActiveRuleParamDto.createFor(rule.getParam(param.getKey()));
  281. paramDto.setValue(param.getValue());
  282. dao.insertParam(dbSession, activeRule, paramDto);
  283. }
  284. }
  285. return activeRule;
  286. }
  287. private ActiveRuleDto doUpdate(ActiveRuleChange change, RuleActivationContext context, DbSession dbSession) {
  288. ActiveRuleWrapper activeRule = context.getActiveRule();
  289. if (activeRule == null) {
  290. return null;
  291. }
  292. ActiveRuleDao dao = db.activeRuleDao();
  293. String severity = change.getSeverity();
  294. if (severity != null) {
  295. activeRule.get().setSeverity(severity);
  296. }
  297. ActiveRuleInheritance inheritance = change.getInheritance();
  298. if (inheritance != null) {
  299. activeRule.get().setInheritance(inheritance.name());
  300. }
  301. activeRule.get().setUpdatedAt(system2.now());
  302. dao.update(dbSession, activeRule.get());
  303. for (Map.Entry<String, String> param : change.getParameters().entrySet()) {
  304. ActiveRuleParamDto activeRuleParamDto = activeRule.getParam(param.getKey());
  305. if (activeRuleParamDto == null) {
  306. // did not exist
  307. if (param.getValue() != null) {
  308. activeRuleParamDto = ActiveRuleParamDto.createFor(context.getRule().getParam(param.getKey()));
  309. activeRuleParamDto.setValue(param.getValue());
  310. dao.insertParam(dbSession, activeRule.get(), activeRuleParamDto);
  311. }
  312. } else {
  313. if (param.getValue() != null) {
  314. activeRuleParamDto.setValue(param.getValue());
  315. dao.updateParam(dbSession, activeRuleParamDto);
  316. } else {
  317. dao.deleteParam(dbSession, activeRuleParamDto);
  318. }
  319. }
  320. }
  321. return activeRule.get();
  322. }
  323. public List<ActiveRuleChange> deactivate(DbSession dbSession, RuleActivationContext context, String ruleUuid, boolean force) {
  324. context.reset(ruleUuid);
  325. return doDeactivate(dbSession, context, force);
  326. }
  327. private List<ActiveRuleChange> doDeactivate(DbSession dbSession, RuleActivationContext context, boolean force) {
  328. List<ActiveRuleChange> changes = new ArrayList<>();
  329. ActiveRuleWrapper activeRule = context.getActiveRule();
  330. if (activeRule == null) {
  331. return changes;
  332. }
  333. ActiveRuleChange change;
  334. checkRequest(force || context.isCascading() || activeRule.get().getInheritance() == null, "Cannot deactivate inherited rule '%s'", context.getRule().get().getKey());
  335. change = new ActiveRuleChange(ActiveRuleChange.Type.DEACTIVATED, activeRule.get(), context.getRule().get());
  336. changes.add(change);
  337. persist(change, context, dbSession);
  338. // get all inherited profiles (they are not built-in by design)
  339. context.getChildProfiles().forEach(child -> {
  340. context.selectChild(child);
  341. changes.addAll(doDeactivate(dbSession, context, force));
  342. });
  343. if (!changes.isEmpty()) {
  344. updateProfileDates(dbSession, context);
  345. }
  346. return changes;
  347. }
  348. @CheckForNull
  349. private String validateParam(RuleParamDto ruleParam, @Nullable String value) {
  350. if (value != null) {
  351. RuleParamType ruleParamType = RuleParamType.parse(ruleParam.getType());
  352. if (ruleParamType.multiple()) {
  353. List<String> values = Splitter.on(",").splitToList(value);
  354. typeValidations.validate(values, ruleParamType.type(), ruleParamType.values());
  355. } else {
  356. typeValidations.validate(value, ruleParamType.type(), ruleParamType.values());
  357. }
  358. }
  359. return value;
  360. }
  361. public RuleActivationContext createContextForBuiltInProfile(DbSession dbSession, RulesProfileDto builtInProfile, Collection<String> ruleUuids) {
  362. checkArgument(builtInProfile.isBuiltIn(), "Rules profile with UUID %s is not built-in", builtInProfile.getUuid());
  363. RuleActivationContext.Builder builder = new RuleActivationContext.Builder();
  364. builder.setDescendantProfilesSupplier(createDescendantProfilesSupplier(dbSession));
  365. // load rules
  366. completeWithRules(dbSession, builder, ruleUuids);
  367. // load org profiles. Their parents are null by nature.
  368. List<QProfileDto> profiles = db.qualityProfileDao().selectQProfilesByRuleProfile(dbSession, builtInProfile);
  369. builder.setProfiles(profiles);
  370. builder.setBaseProfile(builtInProfile);
  371. // load active rules
  372. Collection<String> ruleProfileUuids = Stream
  373. .concat(Stream.of(builtInProfile.getUuid()), profiles.stream().map(QProfileDto::getRulesProfileUuid))
  374. .collect(MoreCollectors.toHashSet(profiles.size() + 1));
  375. completeWithActiveRules(dbSession, builder, ruleUuids, ruleProfileUuids);
  376. return builder.build();
  377. }
  378. public RuleActivationContext createContextForUserProfile(DbSession dbSession, QProfileDto profile, Collection<String> ruleUuids) {
  379. checkArgument(!profile.isBuiltIn(), "Profile with UUID %s is built-in", profile.getKee());
  380. RuleActivationContext.Builder builder = new RuleActivationContext.Builder();
  381. builder.setDescendantProfilesSupplier(createDescendantProfilesSupplier(dbSession));
  382. // load rules
  383. completeWithRules(dbSession, builder, ruleUuids);
  384. // load profiles
  385. List<QProfileDto> profiles = new ArrayList<>();
  386. profiles.add(profile);
  387. if (profile.getParentKee() != null) {
  388. profiles.add(db.qualityProfileDao().selectByUuid(dbSession, profile.getParentKee()));
  389. }
  390. builder.setProfiles(profiles);
  391. builder.setBaseProfile(RulesProfileDto.from(profile));
  392. // load active rules
  393. Collection<String> ruleProfileUuids = profiles.stream()
  394. .map(QProfileDto::getRulesProfileUuid)
  395. .collect(MoreCollectors.toHashSet(profiles.size()));
  396. completeWithActiveRules(dbSession, builder, ruleUuids, ruleProfileUuids);
  397. return builder.build();
  398. }
  399. DescendantProfilesSupplier createDescendantProfilesSupplier(DbSession dbSession) {
  400. return (parents, ruleUuids) -> {
  401. Collection<QProfileDto> profiles = db.qualityProfileDao().selectDescendants(dbSession, parents);
  402. Set<String> ruleProfileUuids = profiles.stream()
  403. .map(QProfileDto::getRulesProfileUuid)
  404. .collect(MoreCollectors.toHashSet());
  405. Collection<ActiveRuleDto> activeRules = db.activeRuleDao().selectByRulesAndRuleProfileUuids(dbSession, ruleUuids, ruleProfileUuids);
  406. List<String> activeRuleUuids = activeRules.stream().map(ActiveRuleDto::getUuid).collect(MoreCollectors.toArrayList(activeRules.size()));
  407. List<ActiveRuleParamDto> activeRuleParams = db.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, activeRuleUuids);
  408. return new DescendantProfilesSupplier.Result(profiles, activeRules, activeRuleParams);
  409. };
  410. }
  411. private void completeWithRules(DbSession dbSession, RuleActivationContext.Builder builder, Collection<String> ruleUuids) {
  412. List<RuleDefinitionDto> rules = db.ruleDao().selectDefinitionByUuids(dbSession, ruleUuids);
  413. builder.setRules(rules);
  414. builder.setRuleParams(db.ruleDao().selectRuleParamsByRuleUuids(dbSession, ruleUuids));
  415. }
  416. private void completeWithActiveRules(DbSession dbSession, RuleActivationContext.Builder builder, Collection<String> ruleUuids, Collection<String> ruleProfileUuids) {
  417. Collection<ActiveRuleDto> activeRules = db.activeRuleDao().selectByRulesAndRuleProfileUuids(dbSession, ruleUuids, ruleProfileUuids);
  418. builder.setActiveRules(activeRules);
  419. List<String> activeRuleUuids = activeRules.stream().map(ActiveRuleDto::getUuid).collect(MoreCollectors.toArrayList(activeRules.size()));
  420. builder.setActiveRuleParams(db.activeRuleDao().selectParamsByActiveRuleUuids(dbSession, activeRuleUuids));
  421. }
  422. private static boolean isSame(ActiveRuleChange change, ActiveRuleWrapper activeRule) {
  423. ActiveRuleInheritance inheritance = change.getInheritance();
  424. if (inheritance != null && !inheritance.name().equals(activeRule.get().getInheritance())) {
  425. return false;
  426. }
  427. String severity = change.getSeverity();
  428. if (severity != null && !severity.equals(activeRule.get().getSeverityString())) {
  429. return false;
  430. }
  431. for (Map.Entry<String, String> changeParam : change.getParameters().entrySet()) {
  432. String activeParamValue = activeRule.getParamValue(changeParam.getKey());
  433. if (changeParam.getValue() == null && activeParamValue != null) {
  434. return false;
  435. }
  436. if (changeParam.getValue() != null && (activeParamValue == null || !StringUtils.equals(changeParam.getValue(), activeParamValue))) {
  437. return false;
  438. }
  439. }
  440. return true;
  441. }
  442. /**
  443. * True if trying to override an inherited rule but with exactly the same values
  444. */
  445. private static boolean isSameAsParent(ActiveRuleChange change, RuleActivationContext context) {
  446. ActiveRuleWrapper parentActiveRule = context.getParentActiveRule();
  447. if (parentActiveRule == null) {
  448. return false;
  449. }
  450. if (!StringUtils.equals(change.getSeverity(), parentActiveRule.get().getSeverityString())) {
  451. return false;
  452. }
  453. for (Map.Entry<String, String> entry : change.getParameters().entrySet()) {
  454. if (entry.getValue() != null && !entry.getValue().equals(parentActiveRule.getParamValue(entry.getKey()))) {
  455. return false;
  456. }
  457. }
  458. return true;
  459. }
  460. @CheckForNull
  461. private static String firstNonNull(String... strings) {
  462. for (String s : strings) {
  463. if (s != null) {
  464. return s;
  465. }
  466. }
  467. return null;
  468. }
  469. }