org.sonar.server.debt.DebtModelOperations.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.server.debt.DebtModelOperations.java

Source

/*
 * 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.debt;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.apache.ibatis.session.SqlSession;
import org.sonar.api.ServerComponent;
import org.sonar.api.server.debt.DebtCharacteristic;
import org.sonar.api.server.debt.internal.DefaultDebtCharacteristic;
import org.sonar.api.utils.System2;
import org.sonar.core.permission.GlobalPermissions;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.rule.RuleDto;
import org.sonar.core.technicaldebt.db.CharacteristicDto;
import org.sonar.server.db.DbClient;
import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.UserSession;
import org.sonar.server.util.Validation;

import javax.annotation.CheckForNull;
import javax.annotation.Nullable;

import java.util.Date;
import java.util.List;

public class DebtModelOperations implements ServerComponent {

    private final DbClient dbClient;
    private final System2 system2;

    public DebtModelOperations(DbClient dbClient) {
        this(dbClient, System2.INSTANCE);
    }

    @VisibleForTesting
    DebtModelOperations(DbClient dbClient, System2 system2) {
        this.dbClient = dbClient;
        this.system2 = system2;
    }

    public DebtCharacteristic create(String name, @Nullable Integer parentId) {
        checkPermission();

        SqlSession session = dbClient.openSession(false);
        try {
            checkNotAlreadyExists(name, session);

            CharacteristicDto newCharacteristic = new CharacteristicDto()
                    .setKey(name.toUpperCase().replace(" ", "_")).setName(name).setEnabled(true)
                    .setCreatedAt(new Date(system2.now()));

            // New sub characteristic
            if (parentId != null) {
                CharacteristicDto parent = findCharacteristic(parentId, session);
                if (parent.getParentId() != null) {
                    throw new BadRequestException(
                            "A sub characteristic can not have a sub characteristic as parent.");
                }
                newCharacteristic.setParentId(parent.getId());
            } else {
                // New root characteristic
                newCharacteristic
                        .setOrder(dbClient.debtCharacteristicDao().selectMaxCharacteristicOrder(session) + 1);
            }
            dbClient.debtCharacteristicDao().insert(newCharacteristic, session);
            session.commit();
            return toCharacteristic(newCharacteristic);
        } finally {
            MyBatis.closeQuietly(session);
        }
    }

    public DebtCharacteristic rename(int characteristicId, String newName) {
        checkPermission();

        SqlSession session = dbClient.openSession(false);
        try {
            checkNotAlreadyExists(newName, session);

            CharacteristicDto dto = findCharacteristic(characteristicId, session);
            if (!dto.getName().equals(newName)) {
                dto.setName(newName);
                dto.setUpdatedAt(new Date(system2.now()));
                dbClient.debtCharacteristicDao().update(dto, session);
                session.commit();
            }
            return toCharacteristic(dto);
        } finally {
            MyBatis.closeQuietly(session);
        }
    }

    public DebtCharacteristic moveUp(int characteristicId) {
        return move(characteristicId, true);
    }

    public DebtCharacteristic moveDown(int characteristicId) {
        return move(characteristicId, false);
    }

    private DebtCharacteristic move(int characteristicId, boolean moveUpOrDown) {
        checkPermission();

        SqlSession session = dbClient.openSession(false);
        try {
            final CharacteristicDto dto = findCharacteristic(characteristicId, session);
            if (dto.getParentId() != null) {
                throw new BadRequestException("Sub characteristics can not be moved.");
            }
            int currentOrder = getOrder(dto);
            CharacteristicDto dtoToSwitchOrderWith = findCharacteristicToSwitchWith(dto, moveUpOrDown, session);

            // Do nothing when characteristic is already to the good location
            if (dtoToSwitchOrderWith == null) {
                return toCharacteristic(dto);
            }

            int nextOrder = getOrder(dtoToSwitchOrderWith);
            dtoToSwitchOrderWith.setOrder(currentOrder);
            dtoToSwitchOrderWith.setUpdatedAt(new Date(system2.now()));
            dbClient.debtCharacteristicDao().update(dtoToSwitchOrderWith, session);

            dto.setOrder(nextOrder);
            dto.setUpdatedAt(new Date(system2.now()));
            dbClient.debtCharacteristicDao().update(dto, session);

            session.commit();
            return toCharacteristic(dto);
        } finally {
            MyBatis.closeQuietly(session);
        }
    }

    private int getOrder(CharacteristicDto characteristicDto) {
        Integer order = characteristicDto.getOrder();
        if (order == null) {
            throw new IllegalArgumentException(String
                    .format("The order of the characteristic '%s' should not be null", characteristicDto.getKey()));
        }
        return order;
    }

    @CheckForNull
    private CharacteristicDto findCharacteristicToSwitchWith(final CharacteristicDto dto,
            final boolean moveUpOrDown, SqlSession session) {
        // characteristics should be sort by 'order'
        List<CharacteristicDto> rootCharacteristics = dbClient.debtCharacteristicDao()
                .selectEnabledRootCharacteristics(session);
        int currentPosition = Iterables.indexOf(rootCharacteristics, new Predicate<CharacteristicDto>() {
            @Override
            public boolean apply(@Nullable CharacteristicDto input) {
                return input != null && input.getKey().equals(dto.getKey());
            }
        });
        Integer nextPosition = moveUpOrDown ? (currentPosition > 0 ? currentPosition - 1 : null)
                : (currentPosition < rootCharacteristics.size() - 1 ? currentPosition + 1 : null);
        return nextPosition != null ? Iterables.get(rootCharacteristics, nextPosition) : null;
    }

    /**
     * Disable characteristic and its sub characteristics or only sub characteristic.
     * Will also update every rules linked to sub characteristics by setting characteristic id to -1 and remove function, coefficient and offset.
     */
    public void delete(int characteristicId) {
        checkPermission();

        Date updateDate = new Date(system2.now());
        DbSession session = dbClient.openSession(true);
        try {
            delete(findCharacteristic(characteristicId, session), updateDate, session);
            session.commit();
        } finally {
            MyBatis.closeQuietly(session);
        }
    }

    /**
     * Disabled a characteristic or a sub characteristic.
     * If it has already been disabled, do nothing (for instance when call on a list of characteristics and sub-characteristics in random order)
     */
    public void delete(CharacteristicDto characteristicOrSubCharacteristic, Date updateDate, DbSession session) {
        // Do nothing is the characteristic is already disabled
        if (characteristicOrSubCharacteristic.isEnabled()) {
            // When root characteristic, browse sub characteristics and disable rule debt on each sub characteristic then disable it
            if (characteristicOrSubCharacteristic.getParentId() == null) {
                List<CharacteristicDto> subCharacteristics = dbClient.debtCharacteristicDao()
                        .selectCharacteristicsByParentId(characteristicOrSubCharacteristic.getId(), session);
                for (CharacteristicDto subCharacteristic : subCharacteristics) {
                    disableSubCharacteristic(subCharacteristic, updateDate, session);
                }
                disableCharacteristic(characteristicOrSubCharacteristic, updateDate, session);
            } else {
                // When sub characteristic, disable rule debt on the sub characteristic then disable it
                disableSubCharacteristic(characteristicOrSubCharacteristic, updateDate, session);
            }
        }
    }

    private void disableSubCharacteristic(CharacteristicDto subCharacteristic, Date updateDate, DbSession session) {
        // Disable debt on all rules (even REMOVED ones, in order to have no issue if they are reactivated) linked to the sub characteristic
        disableRulesDebt(dbClient.ruleDao().findRulesByDebtSubCharacteristicId(session, subCharacteristic.getId()),
                subCharacteristic.getId(), updateDate, session);
        disableCharacteristic(subCharacteristic, updateDate, session);
    }

    private void disableCharacteristic(CharacteristicDto characteristic, Date updateDate, SqlSession session) {
        characteristic.setEnabled(false);
        characteristic.setUpdatedAt(updateDate);
        dbClient.debtCharacteristicDao().update(characteristic, session);
    }

    private void disableRulesDebt(List<RuleDto> ruleDtos, Integer subCharacteristicId, Date updateDate,
            DbSession session) {
        for (RuleDto ruleDto : ruleDtos) {
            if (subCharacteristicId.equals(ruleDto.getSubCharacteristicId())) {
                ruleDto.setSubCharacteristicId(RuleDto.DISABLED_CHARACTERISTIC_ID);
                ruleDto.setRemediationFunction(null);
                ruleDto.setRemediationCoefficient(null);
                ruleDto.setRemediationOffset(null);
                ruleDto.setUpdatedAt(updateDate);
            }
            if (subCharacteristicId.equals(ruleDto.getDefaultSubCharacteristicId())) {
                ruleDto.setDefaultSubCharacteristicId(null);
                ruleDto.setDefaultRemediationFunction(null);
                ruleDto.setDefaultRemediationCoefficient(null);
                ruleDto.setDefaultRemediationOffset(null);
            }
            dbClient.ruleDao().update(session, ruleDto);
        }
    }

    private CharacteristicDto findCharacteristic(Integer id, SqlSession session) {
        CharacteristicDto dto = dbClient.debtCharacteristicDao().selectById(id, session);
        if (dto == null) {
            throw new NotFoundException(String.format("Characteristic with id %s does not exists.", id));
        }
        return dto;
    }

    private void checkNotAlreadyExists(String name, SqlSession session) {
        if (dbClient.debtCharacteristicDao().selectByName(name, session) != null) {
            throw new BadRequestException(Validation.IS_ALREADY_USED_MESSAGE, name);
        }
    }

    private void checkPermission() {
        UserSession.get().checkGlobalPermission(GlobalPermissions.SYSTEM_ADMIN);
    }

    private static DebtCharacteristic toCharacteristic(CharacteristicDto dto) {
        return new DefaultDebtCharacteristic().setId(dto.getId()).setKey(dto.getKey()).setName(dto.getName())
                .setOrder(dto.getOrder()).setParentId(dto.getParentId()).setCreatedAt(dto.getCreatedAt())
                .setUpdatedAt(dto.getUpdatedAt());
    }

}