private List<ActiveRuleChange> cascadeDeactivation(
      ActiveRuleKey key, DbSession dbSession, boolean isCascade, boolean force) {
    List<ActiveRuleChange> changes = Lists.newArrayList();
    RuleActivatorContext context = contextFactory.create(key, dbSession);
    ActiveRuleChange change;
    if (context.activeRule() == null) {
      return changes;
    }
    if (!force && !isCascade && context.activeRule().getInheritance() != null) {
      throw new IllegalStateException("Cannot deactivate inherited rule '" + key.ruleKey() + "'");
    }
    change = ActiveRuleChange.createFor(ActiveRuleChange.Type.DEACTIVATED, key);
    changes.add(change);
    persist(change, context, dbSession);

    // get all inherited profiles
    List<QualityProfileDto> profiles =
        db.qualityProfileDao().findByParentKey(dbSession, key.qProfile());

    for (QualityProfileDto profile : profiles) {
      ActiveRuleKey activeRuleKey = ActiveRuleKey.of(profile.getKey(), key.ruleKey());
      changes.addAll(cascadeDeactivation(activeRuleKey, dbSession, true, force));
    }

    if (!changes.isEmpty()) {
      log.write(dbSession, Activity.Type.ACTIVE_RULE, changes);
      previewCache.reportGlobalModification();
    }

    return changes;
  }
  public List<ActiveRuleChange> activate(DbSession dbSession, RuleActivation activation) {
    RuleActivatorContext context = contextFactory.create(activation.getKey(), dbSession);
    context.verifyForActivation();
    List<ActiveRuleChange> changes = Lists.newArrayList();
    ActiveRuleChange change;
    boolean stopPropagation = false;

    if (context.activeRule() == null) {
      // new activation
      change = ActiveRuleChange.createFor(ActiveRuleChange.Type.ACTIVATED, activation.getKey());
      if (activation.isCascade() || context.isSameAsParent(activation)) {
        change.setInheritance(ActiveRule.Inheritance.INHERITED);
      }
      applySeverityAndParamToChange(activation, context, change);

    } else {
      // already activated

      if (activation.isCascade() && context.activeRule().doesOverride()) {
        // propagating to descendants, but child profile already overrides rule -> stop propagation
        return changes;
      }
      change = ActiveRuleChange.createFor(ActiveRuleChange.Type.UPDATED, activation.getKey());
      if (activation.isCascade() && context.activeRule().getInheritance() == null) {
        // activate on child, then on parent -> mark child as overriding parent
        change.setInheritance(ActiveRule.Inheritance.OVERRIDES);
        change.setSeverity(context.activeRule().getSeverityString());
        change.setParameters(context.activeRuleParamsAsStringMap());
        stopPropagation = true;
      } else {
        applySeverityAndParamToChange(activation, context, change);
        if (!activation.isCascade() && context.parentProfile() != null) {
          // override rule which is already declared on parents
          change.setInheritance(
              context.isSameAsParent(activation)
                  ? ActiveRule.Inheritance.INHERITED
                  : ActiveRule.Inheritance.OVERRIDES);
        }
      }
    }

    changes.add(change);
    persist(change, context, dbSession);

    if (!stopPropagation) {
      changes.addAll(cascadeActivation(dbSession, activation));
    }

    if (!changes.isEmpty()) {
      log.write(dbSession, Activity.Type.ACTIVE_RULE, changes);
      previewCache.reportGlobalModification();
    }
    return changes;
  }