import org.sonar.core.source.db.SnapshotDataDao;
import org.sonar.core.source.db.SnapshotSourceDao;
import org.sonar.core.technicaldebt.db.CharacteristicDao;
+import org.sonar.core.technicaldebt.db.RequirementDao;
import org.sonar.core.template.LoadedTemplateDao;
import org.sonar.core.user.*;
QualityProfileDao.class,
PurgeDao.class,
CharacteristicDao.class,
+ RequirementDao.class,
ResourceIndexerDao.class,
ResourceDao.class,
ResourceKeyUpdaterDao.class,
*/
public class DatabaseVersion implements BatchComponent, ServerComponent {
- public static final int LAST_VERSION = 522;
+ public static final int LAST_VERSION = 520;
public static enum Status {
UP_TO_DATE, REQUIRES_UPGRADE, REQUIRES_DOWNGRADE, FRESH_INSTALL
import org.sonar.core.source.db.SnapshotSourceMapper;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
import org.sonar.core.technicaldebt.db.CharacteristicMapper;
+import org.sonar.core.technicaldebt.db.RequirementDto;
+import org.sonar.core.technicaldebt.db.RequirementMapper;
import org.sonar.core.template.LoadedTemplateDto;
import org.sonar.core.template.LoadedTemplateMapper;
import org.sonar.core.user.*;
loadAlias(conf, "QualityProfile", QualityProfileDto.class);
loadAlias(conf, "ActiveRule", ActiveRuleDto.class);
loadAlias(conf, "ActiveRuleParam", ActiveRuleParamDto.class);
+ loadAlias(conf, "Requirement", RequirementDto.class);
// AuthorizationMapper has to be loaded before IssueMapper because this last one used it
loadMapper(conf, "org.sonar.core.user.AuthorizationMapper");
MeasureMapper.class, SnapshotDataMapper.class, SnapshotSourceMapper.class, ActionPlanMapper.class, ActionPlanStatsMapper.class,
NotificationQueueMapper.class, CharacteristicMapper.class, RuleTagMapper.class,
GroupMembershipMapper.class, QualityProfileMapper.class, ActiveRuleMapper.class,
- MeasureDataMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, ProjectQgateAssociationMapper.class
+ MeasureDataMapper.class, QualityGateMapper.class, QualityGateConditionMapper.class, ComponentMapper.class, ProjectQgateAssociationMapper.class,
+ RequirementMapper.class
};
loadMappers(conf, mappers);
configureLogback(mappers);
public List<RuleDto> selectAll() {
SqlSession session = mybatis.openSession();
try {
- return getMapper(session).selectAll();
+ return selectAll(session);
} finally {
MyBatis.closeQuietly(session);
}
}
+ public List<RuleDto> selectAll(SqlSession session) {
+ return getMapper(session).selectAll();
+ }
+
public List<RuleDto> selectEnablesAndNonManual() {
SqlSession session = mybatis.openSession();
try {
import java.util.Date;
public final class RuleDto {
+
+ public final static Integer DISABLED_CHARACTERISTIC_ID = -1;
+
private Integer id;
private String repositoryKey;
private String ruleKey;
return this;
}
+ public boolean isCharacteristicOverridden(){
+ return !DISABLED_CHARACTERISTIC_ID.equals(characteristicId);
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof RuleDto)) {
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.technicaldebt.db;
+
+import org.apache.ibatis.session.SqlSession;
+import org.sonar.api.ServerComponent;
+import org.sonar.core.persistence.MyBatis;
+
+import java.util.List;
+
+public class RequirementDao implements ServerComponent {
+
+ private final MyBatis mybatis;
+
+ public RequirementDao(MyBatis mybatis) {
+ this.mybatis = mybatis;
+ }
+
+ public List<RequirementDto> selectRequirements() {
+ SqlSession session = mybatis.openSession();
+ try {
+ return selectRequirements(session);
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
+ public List<RequirementDto> selectRequirements(SqlSession session) {
+ return session.getMapper(RequirementMapper.class).selectRequirements();
+ }
+
+
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.technicaldebt.db;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.io.Serializable;
+import java.util.Date;
+
+public class RequirementDto implements Serializable {
+
+ public static final String DAYS = "d";
+ public static final String MINUTES = "mn";
+ public static final String HOURS = "h";
+
+ private Integer id;
+ private Integer parentId;
+ private Integer rootId;
+ private Integer ruleId;
+ private String functionKey;
+ private Double factorValue;
+ private String factorUnit;
+ private Double offsetValue;
+ private String offsetUnit;
+ private Date createdAt;
+ private Date updatedAt;
+ private boolean enabled;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public RequirementDto setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public Integer getParentId() {
+ return parentId;
+ }
+
+ public RequirementDto setParentId(Integer i) {
+ this.parentId = i;
+ return this;
+ }
+
+ public Integer getRootId() {
+ return rootId;
+ }
+
+ public RequirementDto setRootId(Integer rootId) {
+ this.rootId = rootId;
+ return this;
+ }
+
+ public Integer getRuleId() {
+ return ruleId;
+ }
+
+ public RequirementDto setRuleId(Integer ruleId) {
+ this.ruleId = ruleId;
+ return this;
+ }
+
+ public String getFunction() {
+ return functionKey;
+ }
+
+ public RequirementDto setFunction(String function) {
+ this.functionKey = function;
+ return this;
+ }
+
+ @CheckForNull
+ public Double getFactorValue() {
+ return factorValue;
+ }
+
+ public RequirementDto setFactorValue(@Nullable Double factor) {
+ this.factorValue = factor;
+ return this;
+ }
+
+ @CheckForNull
+ public String getFactorUnit() {
+ return factorUnit;
+ }
+
+ public RequirementDto setFactorUnit(@Nullable String factorUnit) {
+ this.factorUnit = factorUnit;
+ return this;
+ }
+
+ @CheckForNull
+ public Double getOffsetValue() {
+ return offsetValue;
+ }
+
+ public RequirementDto setOffsetValue(@Nullable Double offset) {
+ this.offsetValue = offset;
+ return this;
+ }
+
+ @CheckForNull
+ public String getOffsetUnit() {
+ return offsetUnit;
+ }
+
+ public RequirementDto setOffsetUnit(@Nullable String offsetUnit) {
+ this.offsetUnit = offsetUnit;
+ return this;
+ }
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public RequirementDto setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+
+ @CheckForNull
+ public Date getUpdatedAt() {
+ return updatedAt;
+ }
+
+ public RequirementDto setUpdatedAt(@Nullable Date updatedAt) {
+ this.updatedAt = updatedAt;
+ return this;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public RequirementDto setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ return this;
+ }
+
+}
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.technicaldebt.db;
+
+import java.util.List;
+
+public interface RequirementMapper {
+
+ List<RequirementDto> selectRequirements();
+
+}
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('518');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('519');
INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('520');
-INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('521');
-INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('522');
INSERT INTO USERS(ID, LOGIN, NAME, EMAIL, CRYPTED_PASSWORD, SALT, CREATED_AT, UPDATED_AT, REMEMBER_TOKEN, REMEMBER_TOKEN_EXPIRES_AT) VALUES (1, 'admin', 'Administrator', '', 'a373a0e667abb2604c1fd571eb4ad47fe8cc0878', '48bc4b0d93179b5103fd3885ea9119498e9d161b', '2011-09-26 22:27:48.0', '2011-09-26 22:27:48.0', null, null);
ALTER TABLE USERS ALTER COLUMN ID RESTART WITH 2;
"KEE" VARCHAR(100),
"NAME" VARCHAR(100),
"PARENT_ID" INTEGER,
+ "ROOT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "FUNCTION_KEY" VARCHAR(100),
+ "FACTOR_VALUE" DOUBLE,
+ "FACTOR_UNIT" VARCHAR(100),
+ "OFFSET_VALUE" DOUBLE,
+ "OFFSET_UNIT" VARCHAR(100),
"CHARACTERISTIC_ORDER" INTEGER,
"ENABLED" BOOLEAN,
"CREATED_AT" TIMESTAMP,
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mappei.dtd">
+
+<mapper namespace="org.sonar.core.technicaldebt.db.RequirementMapper">
+
+ <sql id="requirementsColumns">
+ c.id,
+ c.parent_id as parentId,
+ c.root_id as rootId,
+ c.rule_id as ruleId,
+ c.function_key as functionKey,
+ c.factor_value as factorValue,
+ c.factor_unit as factorUnit,
+ c.offset_value as offsetValue,
+ c.offset_unit as offsetUnit,
+ c.enabled as enabled,
+ c.created_at as createdAt,
+ c.updated_at as updatedAt
+ </sql>
+
+ <select id="selectRequirements" parameterType="map" resultType="Requirement">
+ select <include refid="requirementsColumns"/>
+ from characteristics c
+ <where>
+ and c.rule_id IS NOT NULL
+ </where>
+ </select>
+
+</mapper>
+
public class CharacteristicDaoTest extends AbstractDaoTestCase {
- private static final String[] EXCLUDED_COLUMNS = new String[]{"id", "created_at", "updated_at"};
+ private static final String[] EXCLUDED_COLUMNS = new String[]{"id", "root_id", "rule_id", "function_key", "factor_unit", "factor_value", "offset_unit", "offset_value",
+ "created_at", "updated_at"};
CharacteristicDao dao;
dao.update(dto);
- checkTables("update_characteristic", new String[]{"id", "depth", "description", "quality_model_id", "updated_at"}, "characteristics");
+ checkTables("update_characteristic", EXCLUDED_COLUMNS, "characteristics");
}
@Test
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.core.technicaldebt.db;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.core.persistence.AbstractDaoTestCase;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class RequirementDaoTest extends AbstractDaoTestCase {
+
+ private static final String[] EXCLUDED_COLUMNS = new String[]{"id", "created_at", "updated_at"};
+
+ RequirementDao dao;
+
+ @Before
+ public void createDao() {
+ dao = new RequirementDao(getMyBatis());
+ }
+
+ @Test
+ public void select_requirements() {
+ setupData("shared");
+
+ assertThat(dao.selectRequirements()).hasSize(2);
+ }
+
+}
--- /dev/null
+<dataset>
+
+ <!-- Requirement -->
+ <characteristics id="3" kee="[null]" name="[null]" parent_id="2" root_id="1" rule_id="10" characteristic_order="[null]"
+ function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <!-- Disabled requirement -->
+ <characteristics id="6" kee="[null]" name="[null]" parent_id="5" root_id="4" rule_id="10" characteristic_order="[null]"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+</dataset>
public static interface InputConverter<S> {
String updateSql();
- void convert(S input, PreparedStatement updateStatement) throws SQLException;
+ /**
+ * Return false if you do not want to update this statement
+ */
+ boolean convert(S input, PreparedStatement updateStatement) throws SQLException;
}
public <S> void execute(InputLoader<S> inputLoader, InputConverter<S> converter) {
int cursor = 0;
while (rs.next()) {
- converter.convert(inputLoader.load(rs), writeStatement);
- writeStatement.addBatch();
+ if (converter.convert(inputLoader.load(rs), writeStatement)) {
+ writeStatement.addBatch();
+ cursor++;
+ count++;
+ }
- cursor++;
- count++;
if (cursor == GROUP_SIZE) {
writeStatement.executeBatch();
writeConnection.commit();
}
@Override
- public void convert(Row row, PreparedStatement updateStatement) throws SQLException {
+ public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException {
updateStatement.setString(1, row.ruleName);
updateStatement.setLong(2, row.issueId);
+ return true;
}
}
);
}
@Override
- public void convert(Row row, PreparedStatement updateStatement) throws SQLException {
+ public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException {
updateStatement.setString(1, convertKey(row.key));
updateStatement.setLong(2, row.id);
+ return true;
}
}
);
import org.sonar.server.db.migrations.SqlUtil;
import javax.annotation.CheckForNull;
+
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
}
@Override
- public void convert(Row row, PreparedStatement updateStatement) throws SQLException {
+ public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException {
updateStatement.setString(1, convertDebtForDays(row.value));
updateStatement.setLong(2, row.id);
+ return true;
}
}
);
}
@Override
- public void convert(Row row, PreparedStatement updateStatement) throws SQLException {
+ public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException {
updateStatement.setString(1, convertChangelog(row.changeData));
updateStatement.setDate(2, new Date(system2.now()));
updateStatement.setLong(3, row.id);
+ return true;
}
}
);
}
@Override
- public void convert(Row row, PreparedStatement updateStatement) throws SQLException {
+ public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException {
updateStatement.setLong(1, workDurationConvertor.createFromLong(row.debt));
updateStatement.setDate(2, new Date(system2.now()));
updateStatement.setLong(3, row.id);
+ return true;
}
}
);
}
@Override
- public void convert(Row row, PreparedStatement updateStatement) throws SQLException {
+ public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException {
setDouble(updateStatement, 1, row.value);
setDouble(updateStatement, 2, row.var1);
setDouble(updateStatement, 3, row.var2);
setDouble(updateStatement, 5, row.var4);
setDouble(updateStatement, 6, row.var5);
updateStatement.setLong(7, row.id);
+ return true;
}
}
);
startupContainer.addSingleton(LogServerId.class);
startupContainer.addSingleton(RegisterServletFilters.class);
startupContainer.addSingleton(CleanPreviewAnalysisCache.class);
+ startupContainer.addSingleton(CopyRequirementsFromCharacteristicsToRules.class);
startupContainer.startComponents();
startupContainer.getComponentByType(ServerLifecycleNotifier.class).notifyStart();
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+package org.sonar.server.startup;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.platform.ServerUpgradeStatus;
+import org.sonar.api.utils.Duration;
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.Database;
+import org.sonar.core.rule.RuleDto;
+import org.sonar.core.technicaldebt.db.RequirementDao;
+import org.sonar.core.technicaldebt.db.RequirementDto;
+import org.sonar.server.db.migrations.MassUpdater;
+import org.sonar.server.db.migrations.SqlUtil;
+import org.sonar.server.rule.RuleRegistration;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+import java.sql.*;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * This script need to be executed after rules registration because default debt columns (characteristics, function, factor and offset) has to be populated
+ * in order to be able to compare default values with overridden values.
+ *
+ * @since 4.3 this component could be removed after 4 or 5 releases.
+ */
+public class CopyRequirementsFromCharacteristicsToRules {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CopyRequirementsFromCharacteristicsToRules.class);
+
+ private final System2 system2;
+
+ private final Database db;
+
+ private final ServerUpgradeStatus status;
+
+ private final RequirementDao requirementDao;
+
+ /**
+ * @param ruleRegistration used only to be started after init of rules
+ */
+ public CopyRequirementsFromCharacteristicsToRules(Database database, RequirementDao requirementDao, ServerUpgradeStatus status, RuleRegistration ruleRegistration) {
+ this(database, requirementDao, status, System2.INSTANCE);
+ }
+
+ @VisibleForTesting
+ CopyRequirementsFromCharacteristicsToRules(Database database, RequirementDao requirementDao, ServerUpgradeStatus status, System2 system2) {
+ this.db = database;
+ this.system2 = system2;
+ this.status = status;
+ this.requirementDao = requirementDao;
+ }
+
+ public void start() {
+ if (mustDoPurge()) {
+ doPurge();
+ }
+ }
+
+ private boolean mustDoPurge() {
+ return status.isUpgraded() && status.getInitialDbVersion() <= 520;
+ }
+
+ private void doPurge() {
+ LOGGER.info("Copying requirement from characteristics to rules");
+ copyRequirementsFromCharacteristicsToRules();
+
+ LOGGER.info("Deleting requirements data");
+ removeRequirementsDataFromCharacteristics();
+ }
+
+ private void copyRequirementsFromCharacteristicsToRules() {
+ List<RequirementDto> requirementDtos = requirementDao.selectRequirements();
+ final Multimap<Integer, RequirementDto> requirementsByRuleId = ArrayListMultimap.create();
+ for (RequirementDto requirementDto : requirementDtos) {
+ requirementsByRuleId.put(requirementDto.getRuleId(), requirementDto);
+ }
+
+ new MassUpdater(db).execute(
+ new MassUpdater.InputLoader<Row>() {
+ @Override
+ public String selectSql() {
+ return "SELECT r.id,r.characteristic_id,r.remediation_function,r.remediation_factor,r.remediation_offset," +
+ "r.default_characteristic_id,r.default_remediation_function,r.default_remediation_factor,r.default_remediation_offset,r.status " +
+ "FROM rules r";
+ }
+
+ @Override
+ public Row load(ResultSet rs) throws SQLException {
+ Row row = new Row();
+ row.id = SqlUtil.getInt(rs, 1);
+ row.characteristicId = SqlUtil.getInt(rs, 2);
+ row.function = rs.getString(3);
+ row.factor = rs.getString(4);
+ row.offset = rs.getString(5);
+ row.defaultCharacteristicId = SqlUtil.getInt(rs, 6);
+ row.defaultFunction = rs.getString(7);
+ row.defaultFactor = rs.getString(8);
+ row.defaultOffset = rs.getString(9);
+ row.status = rs.getString(10);
+ return row;
+ }
+ },
+ new MassUpdater.InputConverter<Row>() {
+ @Override
+ public String updateSql() {
+ return "UPDATE rules SET characteristic_id=?,remediation_function=?,remediation_factor=?,remediation_offset=?,updated_at=? WHERE id=?";
+ }
+
+ @Override
+ public boolean convert(Row row, PreparedStatement updateStatement) throws SQLException {
+ Collection<RequirementDto> requirementsForCurrentRule = requirementsByRuleId.get(row.id);
+
+ if (requirementsForCurrentRule.isEmpty()) {
+ // Nothing to do, there's no requirement on this rule
+ return false;
+
+ } else {
+ RequirementDto enabledRequirement = Iterables.find(requirementsForCurrentRule, new Predicate<RequirementDto>() {
+ @Override
+ public boolean apply(RequirementDto input) {
+ return input.isEnabled();
+ }
+ }, null);
+
+ if (enabledRequirement == null && !"REMOVED".equals(row.getStatus())) {
+ // If no requirements are enable, it means that the requirement has been disabled for this rule
+ updateStatement.setInt(1, RuleDto.DISABLED_CHARACTERISTIC_ID);
+ updateStatement.setNull(2, Types.VARCHAR);
+ updateStatement.setNull(3, Types.VARCHAR);
+ updateStatement.setNull(4, Types.VARCHAR);
+ updateStatement.setDate(5, new Date(system2.now()));
+ updateStatement.setInt(6, row.getId());
+ return true;
+
+ } else if (enabledRequirement != null) {
+ // If one requirement is enable, it means either that this requirement has been set from SQALE, or that it come from a XML model definition
+
+ row.setCharacteristicId(enabledRequirement.getParentId());
+ row.setFunction(enabledRequirement.getFunction().toUpperCase());
+ row.setFactor(convertDuration(enabledRequirement.getFactorValue(), enabledRequirement.getFactorUnit()));
+ row.setOffset(convertDuration(enabledRequirement.getOffsetValue(), enabledRequirement.getOffsetUnit()));
+
+ if (isDebtDefaultValuesSameAsOverriddenValues(row)) {
+ // Default values on debt are the same that ones set by SQALE, nothing to do
+ return false;
+ } else {
+ // Default values on debt are not the same that ones set by SQALE, update the rule
+ updateStatement.setInt(1, row.getCharacteristicId());
+ updateStatement.setString(2, row.getFunction());
+ updateStatement.setString(3, row.getFactor());
+ updateStatement.setString(4, row.getOffset());
+ updateStatement.setDate(5, new Date(system2.now()));
+ updateStatement.setInt(6, row.getId());
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+ );
+ }
+
+ @CheckForNull
+ @VisibleForTesting
+ String convertDuration(@Nullable Double oldValue, @Nullable String oldUnit) {
+ if (oldValue != null) {
+ String unit = oldUnit != null ? oldUnit : RequirementDto.DAYS;
+ // min is replaced by mn
+ unit = RequirementDto.MINUTES.equals(unit) ? Duration.MINUTE : unit;
+ // As value is stored in double, we have to round it in order to have an integer (for instance, if it was 1.6, we'll use 2)
+ return Integer.toString((int) Math.round(oldValue)) + unit;
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ boolean isDebtDefaultValuesSameAsOverriddenValues(Row row) {
+ return new EqualsBuilder()
+ .append(row.getDefaultCharacteristicId(), row.getCharacteristicId())
+ .append(row.getDefaultFunction(), row.getFunction())
+ .append(row.getDefaultFactor(), row.getFactor())
+ .append(row.getDefaultOffset(), row.getOffset())
+ .isEquals();
+ }
+
+
+ private void removeRequirementsDataFromCharacteristics(){
+ try {
+ Connection connection = db.getDataSource().getConnection();
+ Statement stmt = connection.createStatement();
+ stmt.executeUpdate("DELETE FROM characteristics WHERE rule_id IS NOT NULL");
+ } catch (SQLException e) {
+ throw new IllegalStateException("Fail to remove requirements data from characteristics");
+ }
+ }
+
+ @VisibleForTesting
+ static class Row {
+ private Integer id;
+ private Integer characteristicId;
+ private Integer defaultCharacteristicId;
+ private String function;
+ private String defaultFunction;
+ private String factor;
+ private String defaultFactor;
+ private String offset;
+ private String defaultOffset;
+ private String status;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public Row setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public Integer getCharacteristicId() {
+ return characteristicId;
+ }
+
+ public Row setCharacteristicId(Integer characteristicId) {
+ this.characteristicId = characteristicId;
+ return this;
+ }
+
+ public Integer getDefaultCharacteristicId() {
+ return defaultCharacteristicId;
+ }
+
+ public Row setDefaultCharacteristicId(Integer defaultCharacteristicId) {
+ this.defaultCharacteristicId = defaultCharacteristicId;
+ return this;
+ }
+
+ public String getFunction() {
+ return function;
+ }
+
+ public Row setFunction(String function) {
+ this.function = function;
+ return this;
+ }
+
+ public String getDefaultFunction() {
+ return defaultFunction;
+ }
+
+ public Row setDefaultFunction(String defaultFunction) {
+ this.defaultFunction = defaultFunction;
+ return this;
+ }
+
+ public String getFactor() {
+ return factor;
+ }
+
+ public Row setFactor(String factor) {
+ this.factor = factor;
+ return this;
+ }
+
+ public String getDefaultFactor() {
+ return defaultFactor;
+ }
+
+ public Row setDefaultFactor(String defaultFactor) {
+ this.defaultFactor = defaultFactor;
+ return this;
+ }
+
+ public String getOffset() {
+ return offset;
+ }
+
+ public Row setOffset(String offset) {
+ this.offset = offset;
+ return this;
+ }
+
+ public String getDefaultOffset() {
+ return defaultOffset;
+ }
+
+ public Row setDefaultOffset(String defaultOffset) {
+ this.defaultOffset = defaultOffset;
+ return this;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public Row setStatus(String status) {
+ this.status = status;
+ return this;
+ }
+ }
+
+}
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# Sonar 4.3
-# SONAR-5056
-#
-class CopyDebtToRules < ActiveRecord::Migration
-
- class Characteristic < ActiveRecord::Base
- end
-
- class Rule < ActiveRecord::Base
- end
-
- def self.up
- Rule.reset_column_information
-
- requirements = Characteristic.all(
- :conditions => ['rule_id IS NOT NULL AND function_key IS NOT NULL AND enabled=?', true]
- )
- requirements.each do |requirement|
- rule = Rule.find_by_id(requirement.rule_id)
- if rule
- rule.characteristic_id = requirement.parent_id
- # functions are now store in upper case
- rule.remediation_function = requirement.function_key.upcase
- rule.remediation_factor = to_new_remediation(requirement.factor_value, requirement.factor_unit)
- rule.remediation_offset = to_new_remediation(requirement.offset_value, requirement.offset_unit)
- rule.save
- end
- end
- end
-
- def self.to_new_remediation(old_value, old_unit)
- if old_value
- unit = old_unit || 'd'
- unit = unit == 'mn' ? 'min' : unit
- # As value is stored in double, we have to round it in order to have an integer (for instance, if it was 1.6, we'll use 2)
- old_value.to_f.ceil.to_s + unit
- else
- '0d'
- end
- end
-
-end
-
+++ /dev/null
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 3 of the License, or (at your option) any later version.
-#
-# SonarQube is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with this program; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-#
-
-#
-# Sonar 4.3
-# SONAR-5056
-#
-class DeleteRequirements < ActiveRecord::Migration
-
- class Characteristic < ActiveRecord::Base
- end
-
- def self.up
- Characteristic.reset_column_information
-
- Characteristic.delete_all('rule_id IS NOT NULL')
-
- # Remove columns on debt
- remove_column('characteristics', 'root_id')
- remove_column('characteristics', 'rule_id')
- remove_column('characteristics', 'function_key')
- remove_column('characteristics', 'factor_value')
- remove_column('characteristics', 'factor_unit')
- remove_column('characteristics', 'offset_value')
- remove_column('characteristics', 'offset_unit')
- end
-
-end
-
--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonar.server.startup;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.platform.ServerUpgradeStatus;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.core.persistence.AbstractDaoTestCase;
+import org.sonar.core.persistence.TestDatabase;
+import org.sonar.core.technicaldebt.db.RequirementDao;
+
+import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class CopyRequirementsFromCharacteristicsToRulesTest extends AbstractDaoTestCase {
+
+ @ClassRule
+ public static TestDatabase db = new TestDatabase().schema(CopyRequirementsFromCharacteristicsToRulesTest.class, "schema.sql");
+
+ @Mock
+ ServerUpgradeStatus status;
+
+ @Mock
+ System2 system2;
+
+ CopyRequirementsFromCharacteristicsToRules service;
+
+ @Before
+ public void setUp() throws Exception {
+ when(system2.now()).thenReturn(DateUtils.parseDate("2014-03-12").getTime());
+ service = new CopyRequirementsFromCharacteristicsToRules(db.database(), new RequirementDao(getMyBatis()), status, system2);
+ }
+
+ @Test
+ public void copy_requirements_from_characteristics_to_rules() throws Exception {
+ setupData("requirements");
+ db.prepareDbUnit(getClass(), "copy_requirements_from_characteristics_to_rules.xml");
+
+ when(status.isUpgraded()).thenReturn(true);
+ when(status.getInitialDbVersion()).thenReturn(498);
+
+ service.start();
+
+ db.assertDbUnit(getClass(), "copy_requirements_from_characteristics_to_rules_result.xml", "rules");
+ }
+
+ @Test
+ public void remove_requirements_data_from_characteristics() throws Exception {
+ db.prepareDbUnit(getClass(), "remove_requirements_data_from_characteristics.xml");
+
+ when(status.isUpgraded()).thenReturn(true);
+ when(status.getInitialDbVersion()).thenReturn(498);
+
+ service.start();
+
+ db.assertDbUnit(getClass(), "remove_requirements_data_from_characteristics_result.xml", "characteristics");
+ }
+
+ @Test
+ public void convert_duration() throws Exception {
+ assertThat(service.convertDuration(1.0, "h")).isEqualTo("1h");
+ assertThat(service.convertDuration(15.0, "d")).isEqualTo("15d");
+ assertThat(service.convertDuration(5.0, "min")).isEqualTo("5min");
+ assertThat(service.convertDuration(5.0, "mn")).isEqualTo("5min");
+
+ assertThat(service.convertDuration(0.9, "h")).isEqualTo("1h");
+ assertThat(service.convertDuration(1.4, "h")).isEqualTo("1h");
+
+ assertThat(service.convertDuration(1.0, null)).isEqualTo("1d");
+ assertThat(service.convertDuration(null, "d")).isNull();
+ }
+
+ @Test
+ public void is_debt_default_values_same_as_overridden_values() throws Exception {
+ assertThat(service.isDebtDefaultValuesSameAsOverriddenValues(new CopyRequirementsFromCharacteristicsToRules.Row()
+ .setDefaultCharacteristicId(1).setCharacteristicId(1)
+ .setDefaultFunction("LINEAR_OFFSET").setFunction("LINEAR_OFFSET")
+ .setDefaultFactor("5h").setFactor("5h")
+ .setDefaultOffset("10min").setOffset("10min")
+ )).isTrue();
+
+ assertThat(service.isDebtDefaultValuesSameAsOverriddenValues(new CopyRequirementsFromCharacteristicsToRules.Row()
+ .setDefaultCharacteristicId(1).setCharacteristicId(2)
+ .setDefaultFunction("LINEAR_OFFSET").setFunction("LINEAR_OFFSET")
+ .setDefaultFactor("5h").setFactor("5h")
+ .setDefaultOffset("10min").setOffset("10min")
+ )).isFalse();
+
+ assertThat(service.isDebtDefaultValuesSameAsOverriddenValues(new CopyRequirementsFromCharacteristicsToRules.Row()
+ .setDefaultCharacteristicId(1).setCharacteristicId(1)
+ .setDefaultFunction("LINEAR_OFFSET").setFunction("LINEAR_OFFSET")
+ .setDefaultFactor("5h").setFactor("4h")
+ .setDefaultOffset("10min").setOffset("5min")
+ )).isFalse();
+
+ assertThat(service.isDebtDefaultValuesSameAsOverriddenValues(new CopyRequirementsFromCharacteristicsToRules.Row()
+ .setDefaultCharacteristicId(1).setCharacteristicId(1)
+ .setDefaultFunction("CONSTANT_ISSUE").setFunction("LINEAR")
+ .setDefaultFactor(null).setFactor("5h")
+ .setDefaultOffset("10min").setOffset(null)
+ )).isFalse();
+ }
+}
--- /dev/null
+<dataset>
+
+ <!-- Rule not linked to a requirement -> Nothing to do -->
+ <rules id="1" plugin_rule_key="UselessImportCheck" plugin_name="squid" name="UselessImportCheck" description="Useless imports should be removed" status="READY"
+ characteristic_id="[null]" default_characteristic_id="10"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
+
+ <!-- Rule linked to a disabled requirement -> Update rule to disable characteristic -->
+ <rules id="2" plugin_rule_key="LeftCurlyBraceStartLineCheck" plugin_name="squid" name="LeftCurlyBraceStartLineCheck" description="Left curly braces should be located at the beginning of lines of code" status="READY"
+ characteristic_id="[null]" default_characteristic_id="10"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
+
+ <!-- Removed rule linked to a disabled requirement -> Do nothing -->
+ <rules id="3" plugin_rule_key="CallToFileDeleteOnExitMethod" plugin_name="squid" name="CallToFileDeleteOnExitMethod" description="CallToFileDeleteOnExitMethod" status="REMOVED"
+ characteristic_id="[null]" default_characteristic_id="10"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
+
+ <!-- Rule linked to one enable requirement, with same value of debt -> Nothing to do -->
+ <rules id="4" plugin_rule_key="ObjectFinalizeOverridenCallsSuperFinalizeCheck" plugin_name="squid" name="ObjectFinalizeOverridenCallsSuperFinalizeCheck" description="super.finalize() should be called at the end of Object.finalize() implementations" status="READY"
+ characteristic_id="[null]" default_characteristic_id="10"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5min"
+ remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
+
+ <!-- Rule linked to one enable requirement, with different value of debt -> Update rule -->
+ <rules id="5" plugin_rule_key="RightCurlyBraceStartLineCheck" plugin_name="squid" name="RightCurlyBraceStartLineCheck" description="Right curly braces should be located at the beginning of lines of code" status="READY"
+ characteristic_id="[null]" default_characteristic_id="20"
+ remediation_function="[null]" default_remediation_function="LINEAR"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="[null]" updated_at="2014-02-19"/>
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <!-- Rule not linked to a requirement -> Nothing to do -->
+ <rules id="1" plugin_rule_key="UselessImportCheck" plugin_name="squid" name="UselessImportCheck" description="Useless imports should be removed" status="READY"
+ characteristic_id="[null]" default_characteristic_id="10"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
+
+ <!-- Rule linked to a disabled requirements -> Update rule to disable characteristic -->
+ <rules id="2" plugin_rule_key="LeftCurlyBraceStartLineCheck" plugin_name="squid" name="LeftCurlyBraceStartLineCheck" description="Left curly braces should be located at the beginning of lines of code" status="READY"
+ characteristic_id="-1" default_characteristic_id="10"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-03-12"/>
+
+ <!-- Removed rule linked to a disabled requirement -> Do nothing -->
+ <rules id="3" plugin_rule_key="CallToFileDeleteOnExitMethod" plugin_name="squid" name="CallToFileDeleteOnExitMethod" description="CallToFileDeleteOnExitMethod" status="REMOVED"
+ characteristic_id="[null]" default_characteristic_id="10"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5d"
+ remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
+
+ <!-- Rule linked to one enable requirement, with same value of debt -> Nothing to do -->
+ <rules id="4" plugin_rule_key="ObjectFinalizeOverridenCallsSuperFinalizeCheck" plugin_name="squid" name="ObjectFinalizeOverridenCallsSuperFinalizeCheck" description="super.finalize() should be called at the end of Object.finalize() implementations" status="READY"
+ characteristic_id="[null]" default_characteristic_id="10"
+ remediation_function="[null]" default_remediation_function="LINEAR_OFFSET"
+ remediation_factor="[null]" default_remediation_factor="5min"
+ remediation_offset="[null]" default_remediation_offset="10h" updated_at="2014-02-19"/>
+
+ <!-- Rule linked to one enable requirement, with different value of debt -> Update rule -->
+ <rules id="5" plugin_rule_key="RightCurlyBraceStartLineCheck" plugin_name="squid" name="RightCurlyBraceStartLineCheck" description="Right curly braces should be located at the beginning of lines of code" status="READY"
+ characteristic_id="10" default_characteristic_id="20"
+ remediation_function="LINEAR_OFFSET" default_remediation_function="LINEAR"
+ remediation_factor="20min" default_remediation_factor="5d"
+ remediation_offset="30h" default_remediation_offset="[null]" updated_at="2014-03-12"/>
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <!-- Requirements to be updated -->
+ <characteristics id="1" parent_id="10" rule_id="2"
+ function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="2" parent_id="10" rule_id="2"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <characteristics id="3" parent_id="10" rule_id="3"
+ function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="4" parent_id="10" rule_id="3"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <characteristics id="5" parent_id="10" rule_id="4"
+ function_key="linear_offset" factor_value="5.0" factor_unit="mn" offset_value="9.8" offset_unit="h" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="6" parent_id="10" rule_id="4"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <characteristics id="7" parent_id="10" rule_id="5"
+ function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="8" parent_id="10" rule_id="5"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <!-- Characteristics not to be updated -->
+
+ <characteristics id="10" parent_id="10" rule_id="[null]"
+ function_key="[null]" factor_value="[null]" factor_unit="[null]" offset_value="[null]" offset_unit="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="11" parent_id="10" rule_id="[null]"
+ function_key="[null]" factor_value="[null]" factor_unit="[null]" offset_value="[null]" offset_unit="[null]" enabled="[false]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+
+</dataset>
--- /dev/null
+<!--
+ ~ SonarQube, open source software quality management tool.
+ ~ Copyright (C) 2008-2014 SonarSource
+ ~ mailto:contact AT sonarsource DOT com
+ ~
+ ~ SonarQube is free software; you can redistribute it and/or
+ ~ modify it under the terms of the GNU Lesser General Public
+ ~ License as published by the Free Software Foundation; either
+ ~ version 3 of the License, or (at your option) any later version.
+ ~
+ ~ SonarQube is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public License
+ ~ along with this program; if not, write to the Free Software Foundation,
+ ~ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ -->
+
+<dataset>
+
+ <!-- Requirements to be updated -->
+ <!--<characteristics id="1" parent_id="10" rule_id="2"-->
+ <!--function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[false]"-->
+ <!--created_at="2013-11-20" updated_at="[null]"/>-->
+
+ <!--<characteristics id="2" parent_id="10" rule_id="2"-->
+ <!--function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"-->
+ <!--created_at="2013-11-20" updated_at="2013-11-22"/>-->
+
+ <!--<characteristics id="3" parent_id="10" rule_id="3"-->
+ <!--function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[false]"-->
+ <!--created_at="2013-11-20" updated_at="[null]"/>-->
+
+ <!--<characteristics id="4" parent_id="10" rule_id="3"-->
+ <!--function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"-->
+ <!--created_at="2013-11-20" updated_at="2013-11-22"/>-->
+
+ <!--<characteristics id="5" parent_id="10" rule_id="4"-->
+ <!--function_key="linear_offset" factor_value="5.0" factor_unit="mn" offset_value="9.8" offset_unit="h" enabled="[true]"-->
+ <!--created_at="2013-11-20" updated_at="[null]"/>-->
+
+ <!--<characteristics id="6" parent_id="10" rule_id="4"-->
+ <!--function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"-->
+ <!--created_at="2013-11-20" updated_at="2013-11-22"/>-->
+
+ <!--<characteristics id="7" parent_id="10" rule_id="5"-->
+ <!--function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[true]"-->
+ <!--created_at="2013-11-20" updated_at="[null]"/>-->
+
+ <!--<characteristics id="8" parent_id="10" rule_id="5"-->
+ <!--function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"-->
+ <!--created_at="2013-11-20" updated_at="2013-11-22"/>-->
+
+ <!-- Characteristics not to be updated -->
+
+ <characteristics id="10" parent_id="10" rule_id="[null]"
+ function_key="[null]" factor_value="[null]" factor_unit="[null]" offset_value="[null]" offset_unit="[null]" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="11" parent_id="10" rule_id="[null]"
+ function_key="[null]" factor_value="[null]" factor_unit="[null]" offset_value="[null]" offset_unit="[null]" enabled="[false]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+
+</dataset>
--- /dev/null
+<dataset>
+
+ <!-- No requirement for rule 1 -->
+
+ <!-- Requirement of rule 2 -->
+ <characteristics id="1" parent_id="10" rule_id="2"
+ function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="2" parent_id="10" rule_id="2"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <!-- Requirement of rule 3 -->
+ <characteristics id="3" parent_id="10" rule_id="3"
+ function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="4" parent_id="10" rule_id="3"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <!-- Requirement of rule 4 -->
+ <characteristics id="5" parent_id="10" rule_id="4"
+ function_key="linear_offset" factor_value="5.0" factor_unit="mn" offset_value="9.8" offset_unit="h" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="6" parent_id="10" rule_id="4"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+ <!-- Requirement of rule 5 -->
+ <characteristics id="7" parent_id="10" rule_id="5"
+ function_key="linear_offset" factor_value="20.0" factor_unit="mn" offset_value="30.0" offset_unit="h" enabled="[true]"
+ created_at="2013-11-20" updated_at="[null]"/>
+
+ <characteristics id="8" parent_id="10" rule_id="5"
+ function_key="linear_offset" factor_value="30.0" factor_unit="mn" offset_value="20.0" offset_unit="h" enabled="[false]"
+ created_at="2013-11-20" updated_at="2013-11-22"/>
+
+</dataset>
--- /dev/null
+CREATE TABLE "RULES" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PLUGIN_RULE_KEY" VARCHAR(200) NOT NULL,
+ "PLUGIN_NAME" VARCHAR(255) NOT NULL,
+ "DESCRIPTION" VARCHAR(16777215),
+ "NAME" VARCHAR(200),
+ "STATUS" VARCHAR(40),
+ "CHARACTERISTIC_ID" INTEGER,
+ "DEFAULT_CHARACTERISTIC_ID" INTEGER,
+ "REMEDIATION_FUNCTION" VARCHAR(20),
+ "DEFAULT_REMEDIATION_FUNCTION" VARCHAR(20),
+ "REMEDIATION_FACTOR" VARCHAR(20),
+ "DEFAULT_REMEDIATION_FACTOR" VARCHAR(20),
+ "REMEDIATION_OFFSET" VARCHAR(20),
+ "DEFAULT_REMEDIATION_OFFSET" VARCHAR(20),
+ "UPDATED_AT" TIMESTAMP
+);
+
+CREATE TABLE "CHARACTERISTICS" (
+ "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+ "PARENT_ID" INTEGER,
+ "RULE_ID" INTEGER,
+ "FUNCTION_KEY" VARCHAR(100),
+ "FACTOR_VALUE" DOUBLE,
+ "FACTOR_UNIT" VARCHAR(100),
+ "OFFSET_VALUE" DOUBLE,
+ "OFFSET_UNIT" VARCHAR(100),
+ "ENABLED" BOOLEAN,
+ "CREATED_AT" TIMESTAMP,
+ "UPDATED_AT" TIMESTAMP
+);