org.sonar.server.issue.IssueFieldsSetter.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.server.issue.IssueFieldsSetter.java

Source

/*
 * SonarQube
 * Copyright (C) 2009-2017 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program 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.
 *
 * This program 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.issue;

import com.google.common.base.Joiner;
import com.google.common.collect.Sets;
import java.time.temporal.ChronoUnit;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.rules.RuleType;
import org.sonar.api.server.ServerSide;
import org.sonar.api.server.rule.RuleTagFormat;
import org.sonar.api.utils.Duration;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.DefaultIssueComment;
import org.sonar.core.issue.IssueChangeContext;
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.user.UserDto;

import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Strings.isNullOrEmpty;

/**
 * Updates issue fields and chooses if changes must be kept in history.
 */
@ServerSide
@ComputeEngineSide
public class IssueFieldsSetter {

    public static final String UNUSED = "";
    public static final String SEVERITY = "severity";
    public static final String TYPE = "type";
    public static final String ASSIGNEE = "assignee";
    public static final String RESOLUTION = "resolution";
    public static final String STATUS = "status";
    public static final String AUTHOR = "author";
    public static final String FILE = "file";

    /**
     * It should be renamed to 'effort', but it hasn't been done to prevent a massive update in database
     */
    public static final String TECHNICAL_DEBT = "technicalDebt";
    public static final String TAGS = "tags";

    private static final Joiner CHANGELOG_TAG_JOINER = Joiner.on(" ").skipNulls();

    public boolean setType(DefaultIssue issue, RuleType type, IssueChangeContext context) {
        if (!Objects.equals(type, issue.type())) {
            issue.setFieldChange(context, TYPE, issue.type(), type);
            issue.setType(type);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            return true;
        }
        return false;
    }

    public boolean setSeverity(DefaultIssue issue, String severity, IssueChangeContext context) {
        checkState(!issue.manualSeverity(), "Severity can't be changed");
        if (!Objects.equals(severity, issue.severity())) {
            issue.setFieldChange(context, SEVERITY, issue.severity(), severity);
            issue.setSeverity(severity);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            return true;
        }
        return false;
    }

    public boolean setPastSeverity(DefaultIssue issue, @Nullable String previousSeverity,
            IssueChangeContext context) {
        String currentSeverity = issue.severity();
        issue.setSeverity(previousSeverity);
        return setSeverity(issue, currentSeverity, context);
    }

    public boolean setManualSeverity(DefaultIssue issue, String severity, IssueChangeContext context) {
        if (!issue.manualSeverity() || !Objects.equals(severity, issue.severity())) {
            issue.setFieldChange(context, SEVERITY, issue.severity(), severity);
            issue.setSeverity(severity);
            issue.setManualSeverity(true);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            issue.setSendNotifications(true);
            return true;
        }
        return false;
    }

    public boolean assign(DefaultIssue issue, @Nullable UserDto user, IssueChangeContext context) {
        String sanitizedAssignee = null;
        if (user != null) {
            sanitizedAssignee = StringUtils.defaultIfBlank(user.getLogin(), null);
        }
        if (!Objects.equals(sanitizedAssignee, issue.assignee())) {
            String newAssigneeName = user != null ? user.getName() : null;
            issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssigneeName);
            issue.setAssignee(sanitizedAssignee);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            issue.setSendNotifications(true);
            return true;
        }
        return false;
    }

    /**
     * Used to set the assignee when it was null
     */
    public boolean setNewAssignee(DefaultIssue issue, @Nullable String newAssignee, IssueChangeContext context) {
        if (newAssignee == null) {
            return false;
        }
        checkState(issue.assignee() == null,
                "It's not possible to update the assignee with this method, please use assign()");
        issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssignee);
        issue.setAssignee(newAssignee);
        issue.setUpdateDate(context.date());
        issue.setChanged(true);
        issue.setSendNotifications(true);
        return true;
    }

    public boolean setLine(DefaultIssue issue, @Nullable Integer line) {
        if (!Objects.equals(line, issue.line())) {
            issue.setLine(line);
            issue.setChanged(true);
            return true;
        }
        return false;
    }

    public boolean setPastLine(DefaultIssue issue, @Nullable Integer previousLine) {
        Integer currentLine = issue.line();
        issue.setLine(previousLine);
        return setLine(issue, currentLine);
    }

    public boolean setLocations(DefaultIssue issue, @Nullable Object locations) {
        if (!Objects.equals(locations, issue.getLocations())) {
            issue.setLocations(locations);
            issue.setChanged(true);
            return true;
        }
        return false;
    }

    public boolean setPastLocations(DefaultIssue issue, @Nullable Object previousLocations) {
        Object currentLocations = issue.getLocations();
        issue.setLocations(previousLocations);
        return setLocations(issue, currentLocations);

    }

    public boolean setResolution(DefaultIssue issue, @Nullable String resolution, IssueChangeContext context) {
        if (!Objects.equals(resolution, issue.resolution())) {
            issue.setFieldChange(context, RESOLUTION, issue.resolution(), resolution);
            issue.setResolution(resolution);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            issue.setSendNotifications(true);
            return true;
        }
        return false;
    }

    public boolean setStatus(DefaultIssue issue, String status, IssueChangeContext context) {
        if (!Objects.equals(status, issue.status())) {
            issue.setFieldChange(context, STATUS, issue.status(), status);
            issue.setStatus(status);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            issue.setSendNotifications(true);
            return true;
        }
        return false;
    }

    public boolean setAuthorLogin(DefaultIssue issue, @Nullable String authorLogin, IssueChangeContext context) {
        if (!Objects.equals(authorLogin, issue.authorLogin())) {
            issue.setFieldChange(context, AUTHOR, issue.authorLogin(), authorLogin);
            issue.setAuthorLogin(authorLogin);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            // do not send notifications to prevent spam when installing the developer cockpit plugin
            return true;
        }
        return false;
    }

    /**
     * Used to set the author when it was null
     */
    public boolean setNewAuthor(DefaultIssue issue, @Nullable String newAuthorLogin, IssueChangeContext context) {
        if (isNullOrEmpty(newAuthorLogin)) {
            return false;
        }
        checkState(issue.authorLogin() == null,
                "It's not possible to update the author with this method, please use setAuthorLogin()");
        issue.setFieldChange(context, AUTHOR, null, newAuthorLogin);
        issue.setAuthorLogin(newAuthorLogin);
        issue.setUpdateDate(context.date());
        issue.setChanged(true);
        // do not send notifications to prevent spam when installing the developer cockpit plugin
        return true;
    }

    public boolean setMessage(DefaultIssue issue, @Nullable String s, IssueChangeContext context) {
        if (!Objects.equals(s, issue.message())) {
            issue.setMessage(s);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            return true;
        }
        return false;
    }

    public boolean setPastMessage(DefaultIssue issue, @Nullable String previousMessage,
            IssueChangeContext context) {
        String currentMessage = issue.message();
        issue.setMessage(previousMessage);
        return setMessage(issue, currentMessage, context);
    }

    public void addComment(DefaultIssue issue, String text, IssueChangeContext context) {
        issue.addComment(DefaultIssueComment.create(issue.key(), context.login(), text));
        issue.setUpdateDate(context.date());
        issue.setChanged(true);
    }

    public void setCloseDate(DefaultIssue issue, @Nullable Date d, IssueChangeContext context) {
        if (relevantDateDifference(d, issue.closeDate())) {
            issue.setCloseDate(d);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
        }
    }

    public void setCreationDate(DefaultIssue issue, Date d, IssueChangeContext context) {
        if (relevantDateDifference(d, issue.creationDate())) {
            issue.setCreationDate(d);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
        }
    }

    public boolean setGap(DefaultIssue issue, @Nullable Double d, IssueChangeContext context) {
        if (!Objects.equals(d, issue.gap())) {
            issue.setGap(d);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            // Do not send notifications to prevent spam when installing the SQALE plugin,
            // and do not complete the changelog (for the moment)
            return true;
        }
        return false;
    }

    public boolean setPastGap(DefaultIssue issue, @Nullable Double previousGap, IssueChangeContext context) {
        Double currentGap = issue.gap();
        issue.setGap(previousGap);
        return setGap(issue, currentGap, context);
    }

    public boolean setEffort(DefaultIssue issue, @Nullable Duration value, IssueChangeContext context) {
        Duration oldValue = issue.effort();
        if (!Objects.equals(value, oldValue)) {
            issue.setEffort(value != null ? value : null);
            issue.setFieldChange(context, TECHNICAL_DEBT, oldValue != null ? oldValue.toMinutes() : null,
                    value != null ? value.toMinutes() : null);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            return true;
        }
        return false;
    }

    public boolean setPastEffort(DefaultIssue issue, @Nullable Duration previousEffort,
            IssueChangeContext context) {
        Duration currentEffort = issue.effort();
        issue.setEffort(previousEffort);
        return setEffort(issue, currentEffort, context);
    }

    public boolean setAttribute(DefaultIssue issue, String key, @Nullable String value,
            IssueChangeContext context) {
        String oldValue = issue.attribute(key);
        if (!Objects.equals(oldValue, value)) {
            issue.setFieldChange(context, key, oldValue, value);
            issue.setAttribute(key, value);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            return true;
        }
        return false;
    }

    public boolean setTags(DefaultIssue issue, Collection<String> tags, IssueChangeContext context) {
        Set<String> newTags = tags.stream().filter(Objects::nonNull).filter(tag -> !tag.isEmpty())
                .map(tag -> RuleTagFormat.validate(tag.toLowerCase(Locale.ENGLISH)))
                .collect(MoreCollectors.toSet());

        Set<String> oldTags = Sets.newHashSet(issue.tags());
        if (!oldTags.equals(newTags)) {
            issue.setFieldChange(context, TAGS, oldTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(oldTags),
                    newTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(newTags));
            issue.setTags(newTags);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            issue.setSendNotifications(true);
            return true;
        }
        return false;
    }

    public boolean setIssueMoved(DefaultIssue issue, String newComponentUuid, IssueChangeContext context) {
        if (!Objects.equals(newComponentUuid, issue.componentUuid())) {
            issue.setFieldChange(context, FILE, issue.componentUuid(), newComponentUuid);
            issue.setComponentUuid(newComponentUuid);
            issue.setUpdateDate(context.date());
            issue.setChanged(true);
            return true;
        }
        return false;
    }

    private static boolean relevantDateDifference(@Nullable Date left, @Nullable Date right) {
        return !Objects.equals(truncateMillis(left), truncateMillis(right));
    }

    private static Date truncateMillis(@Nullable Date d) {
        if (d == null) {
            return null;
        }
        return Date.from(d.toInstant().truncatedTo(ChronoUnit.SECONDS));
    }
}