@VisibleForTesting
  List<CharacteristicDto> restoreCharacteristics(
      DebtModel targetModel, Date updateDate, DbSession session) {
    List<CharacteristicDto> sourceCharacteristics =
        dbClient.debtCharacteristicDao().selectEnabledCharacteristics(session);

    List<CharacteristicDto> result = newArrayList();

    // Create new characteristics
    for (DebtCharacteristic characteristic : targetModel.rootCharacteristics()) {
      CharacteristicDto rootCharacteristicDto =
          restoreCharacteristic(characteristic, null, sourceCharacteristics, updateDate, session);
      result.add(rootCharacteristicDto);
      for (DebtCharacteristic subCharacteristic :
          targetModel.subCharacteristics(characteristic.key())) {
        result.add(
            restoreCharacteristic(
                subCharacteristic,
                rootCharacteristicDto.getId(),
                sourceCharacteristics,
                updateDate,
                session));
      }
    }
    // Disable no more existing characteristics
    for (CharacteristicDto sourceCharacteristic : sourceCharacteristics) {
      if (targetModel.characteristicByKey(sourceCharacteristic.getKey()) == null) {
        debtModelOperations.delete(sourceCharacteristic, updateDate, session);
      }
    }
    return result;
  }
  private String backupFromLanguage(@Nullable String languageKey) {
    checkPermission();

    DbSession session = dbClient.openSession(false);
    try {
      DebtModel debtModel = new DebtModel();
      List<CharacteristicDto> characteristicDtos =
          dbClient.debtCharacteristicDao().selectEnabledCharacteristics(session);
      for (CharacteristicDto characteristicDto : characteristicDtos) {
        if (characteristicDto.getParentId() == null) {
          debtModel.addRootCharacteristic(toDebtCharacteristic(characteristicDto));
          for (CharacteristicDto sub :
              subCharacteristics(characteristicDto.getId(), characteristicDtos)) {
            debtModel.addSubCharacteristic(toDebtCharacteristic(sub), characteristicDto.getKey());
          }
        }
      }

      List<RuleDebt> rules = newArrayList();
      for (RuleDto rule : dbClient.ruleDao().selectEnabledAndNonManual(session)) {
        if (languageKey == null || languageKey.equals(rule.getLanguage())) {
          RuleDebt ruleDebt = toRuleDebt(rule, debtModel);
          if (ruleDebt != null) {
            rules.add(ruleDebt);
          }
        }
      }
      return debtModelXMLExporter.export(debtModel, rules);
    } finally {
      MyBatis.closeQuietly(session);
    }
  }
  @CheckForNull
  private static RuleDebt toRuleDebt(RuleDto rule, DebtModel debtModel) {
    RuleDebt ruleDebt =
        new RuleDebt().setRuleKey(RuleKey.of(rule.getRepositoryKey(), rule.getRuleKey()));
    Integer effectiveSubCharacteristicId =
        rule.getSubCharacteristicId() != null
            ? rule.getSubCharacteristicId()
            : rule.getDefaultSubCharacteristicId();
    DebtCharacteristic subCharacteristic =
        (effectiveSubCharacteristicId != null
                && !RuleDto.DISABLED_CHARACTERISTIC_ID.equals(effectiveSubCharacteristicId))
            ? debtModel.characteristicById(effectiveSubCharacteristicId)
            : null;
    if (subCharacteristic != null) {
      ruleDebt.setSubCharacteristicKey(subCharacteristic.key());

      String overriddenFunction = rule.getRemediationFunction();
      String defaultFunction = rule.getDefaultRemediationFunction();
      if (overriddenFunction != null) {
        ruleDebt.setFunction(overriddenFunction);
        ruleDebt.setCoefficient(rule.getRemediationCoefficient());
        ruleDebt.setOffset(rule.getRemediationOffset());
        return ruleDebt;
      } else if (defaultFunction != null) {
        ruleDebt.setFunction(defaultFunction);
        ruleDebt.setCoefficient(rule.getDefaultRemediationCoefficient());
        ruleDebt.setOffset(rule.getDefaultRemediationOffset());
        return ruleDebt;
      }
    }
    return null;
  }