All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cdc.issues.Issue Maven / Gradle / Ivy

package cdc.issues;

import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;

import cdc.issues.locations.LocatedItem;
import cdc.issues.locations.Location;
import cdc.issues.rules.Rule;
import cdc.issues.rules.RuleId;
import cdc.util.lang.Checks;

/**
 * Base class used to describe an Issue (more exactly an occurrence of
 * detection of an issue).
 * 

* Node: This class can be specialized if necessary. *

* An issue may have several target locations.
* For example, when there is a compliance issue between several things, * one can not tell which one is at fault. *

* WARNNING
* Issue should probably NOT be derived.
* Currently, IO implementations only produce the base Issue class. * There is no support to build any derived class. *

* If you however do so, it is highly recommended not to add any attribute. * If you do that, those attributes won't be retrieved by IO. * * @author Damien Carbonne */ public class Issue { private static final String DESCRIPTION = "description"; private static final String LABELS = "labels"; private static final String LOCATION = "location"; private static final String LOCATIONS = "locations"; private static final String METAS = "metas"; private static final String PARAMS = "params"; private static final String SEVERITY = "severity"; /** * Comparator of Issues using timestamp. */ public static final Comparator TIMESTAMP_COMPARATOR = Comparator.comparing(Issue::getTimestamp); private final Instant timestamp; private final IssueId id; private final String snapshot; private final IssueSeverity severity; private final String description; private final Metas metas; private final Labels labels; protected Issue(Builder builder) { this.timestamp = builder.timestamp == null ? Instant.now() : builder.timestamp; this.id = IssueId.builder() .domain(builder.domain) .name(builder.name) .params(builder.params) .project(builder.project) .locations(builder.locations) .build(); this.snapshot = builder.snapshot; this.severity = Checks.isNotNull(builder.severity, SEVERITY); this.description = Checks.isNotNull(builder.description, DESCRIPTION); this.metas = Checks.isNotNull(builder.metas, METAS); this.labels = Checks.isNotNull(builder.labels, LABELS); } /** * Creates an Issue. * * @param timestamp The optional timestamp. * @param domain The rule domain. * @param name The rule name. * @param params The rule parameters. * @param project The project name. * @param locations The target locations. * @param snapshot The issue snapshot. * @param severity The issue severity. * @param description The issue description. * @param metas The issue meta data. * @deprecated Do not use anymore. Issue will be made final. */ @Deprecated(forRemoval = true, since = "2023-10-14") protected Issue(Instant timestamp, String domain, String name, Params params, String project, List locations, String snapshot, IssueSeverity severity, String description, Params metas) { this.timestamp = timestamp == null ? Instant.now() : timestamp; this.id = IssueId.builder() .domain(domain) .name(name) .params(params) .project(project) .locations(locations) .build(); this.snapshot = snapshot; this.severity = Checks.isNotNull(severity, SEVERITY); this.description = Checks.isNotNull(description, DESCRIPTION); this.metas = Checks.isNotNull(Metas.of(metas), METAS); this.labels = Labels.NO_LABELS; } /** * * @param domain The rule domain. * @param name The rule name. * @param params The rule parameters. * @param project The project name. * @param locations The target locations. * @param snapshot The issue snapshot. * @param severity The issue severity. * @param description The issue description. * @param metas The issue meta data. * @deprecated Do not use anymore. Issue will be made final. */ @Deprecated(forRemoval = true, since = "2023-10-14") protected Issue(String domain, String name, Params params, String project, List locations, String snapshot, IssueSeverity severity, String description, Params metas) { this(null, domain, name, params, project, locations, snapshot, severity, description, metas); } /** * * @param timestamp The optional timestamp. * @param domain The rule domain. * @param name The rule name. * @param params The rule parameters. * @param project The project name. * @param locations The target locations. * @param snapshot The issue snapshot. * @param severity The issue severity. * @param description The issue description. * @param metas The issue meta data. * @deprecated Do not use anymore. Issue will be made final. */ @Deprecated(forRemoval = true, since = "2023-10-14") protected Issue(Instant timestamp, String domain, Enum name, Params params, String project, List locations, String snapshot, IssueSeverity severity, String description, Params metas) { this(timestamp, domain, name.name(), params, project, locations, snapshot, severity, description, metas); } /** * * @param domain The rule domain. * @param name The rule name. * @param params The rule parameters. * @param project The project name. * @param locations The target locations. * @param snapshot The issue snapshot. * @param severity The issue severity. * @param description The issue description. * @param metas The issue meta data. * @deprecated Do not use anymore. Issue will be made final. */ @Deprecated(forRemoval = true, since = "2023-10-14") protected Issue(String domain, Enum name, Params params, String project, List locations, String snapshot, IssueSeverity severity, String description, Params metas) { this(null, domain, name, params, project, locations, snapshot, severity, description, metas); } /** * @return The Instant at which this issue was created. */ public final Instant getTimestamp() { return timestamp; } /** * @return The id of this issue. */ public IssueId getId() { return id; } /** * @return The RuleId of this issue. */ public RuleId getRuleId() { return id.getRuleId(); } /** * @return The domain of the rule of this issue. */ public String getDomain() { return id.getDomain(); } /** * @return The name of the rule of this issue. */ public String getName() { return id.getName(); } public > T getName(Class typeClass) { return id.getName(typeClass); } /** * @return The rule parameters of this issue. */ public Params getParams() { return id.getParams(); } /** * @return The project of this issue. * May be {@code null}. */ public String getProject() { return id.getProject(); } /** * @return The snapshot of this issue. * May be {@code null}. */ public String getSnapshot() { return snapshot; } /** * @return The severity of this issue. */ public final IssueSeverity getSeverity() { return severity; } /** * @return The description of this issue. */ public final String getDescription() { return description; } /** * @return The meta data associated to this issue. */ public Metas getMetas() { return metas; } /** * @return The labels associated to this issue. */ public Labels getLabels() { return labels; } /** * @return The target locations of this issue. */ public Location[] getLocations() { return id.getLocations(); } /** * @return The number of target locations of this issue. */ public final int getNumberOfLocations() { return id.getLocations().length; } public Location getLocationAt(int index) { return id.getLocations()[index]; } public L getLocationAt(int index, Class cls) { return cls.cast(getLocationAt(index)); } @Override public int hashCode() { return Objects.hash(timestamp, id, snapshot, severity, description, metas, labels); } @Override public boolean equals(Object object) { if (this == object) { return true; } if (object == null || getClass() != object.getClass()) { return false; } final Issue other = (Issue) object; return Objects.equals(this.timestamp, other.timestamp) && Objects.equals(this.id, other.id) && Objects.equals(this.snapshot, other.snapshot) && this.severity == other.severity && Objects.equals(this.description, other.description) && Objects.equals(this.metas, other.metas) && Objects.equals(this.labels, other.labels); } @Override public String toString() { return getTimestamp() + " - " + getDomain() + " - " + getName() + " - " + getParams() + " - " + getProject() + " - " + getSnapshot() + " - " + getSeverity() + " - " + getDescription() + " - " + Arrays.toString(getLocations()) + " - " + getMetas() + " - " + getLabels(); } /** * @return A new {@link Builder} of {@link Issue}. */ public static Builder builder() { return new Builder<>(); } /** * Builder of {@link Issue}. *

* This class should be derived to construct specializations of {@link Issue}. * * @param The builder type. */ public static class Builder> { protected Instant timestamp = null; protected String domain; protected String name; protected Params params = Params.NO_PARAMS; protected String project; protected String snapshot; protected IssueSeverity severity; protected String description = ""; protected final List locations = new ArrayList<>(); protected Metas metas = Metas.NO_METAS; protected Labels labels = Labels.NO_LABELS; protected Builder() { } @SuppressWarnings("unchecked") protected B self() { return (B) this; } public B accept(Consumer consumer) { consumer.accept(self()); return self(); } /** * Sets the issue timestamp. *

* WARNING: this should only be used to reconstruct an issue -from a file, stream, ...). * * @param timestamp The timestamp. * @return This builder. */ public B timestamp(Instant timestamp) { this.timestamp = timestamp; return self(); } /** * Sets the issue Rule. *

* This is equivalent to setting its domain, name, and severity. * * @param rule The rule. * @return This builder. */ public B rule(Rule rule) { this.domain = rule.getDomain(); this.name = rule.getName(); this.severity = rule.getSeverity(); return self(); } /** * Sets the issue RuleId. *

* This is equivalent to setting its domain and name. * * @param ruleId The rule id. * @return This builder. */ public B ruleId(RuleId ruleId) { this.domain = ruleId.getDomain(); this.name = ruleId.getName(); return self(); } /** * Sets the issue domain. * * @param domain The domain. * @return This builder. */ public B domain(String domain) { this.domain = domain; return self(); } /** * Sets the issue name. * * @param name The name. * @return This builder. */ public B name(String name) { this.name = name; return self(); } /** * Sets the issue name, and optionally its severity if the {@code name} implements * {@link IssueSeverityItem} and current severity is {@code null}. * * @param name The name. * @return This builder. */ public B name(Enum name) { this.name = name.name(); if (severity == null && name instanceof final IssueSeverityItem isi) { final IssueSeverity s = isi.getSeverity(); if (s != null) { severity(s); } } return self(); } public B params(Params params) { Checks.isNotNull(params, PARAMS); this.params = params; return self(); } public B labels(Labels labels) { Checks.isNotNull(labels, LABELS); this.labels = labels; return self(); } /** * Sets the project name (may be {@code null}). * * @param project The project name. * @return This builder. */ public B project(String project) { this.project = project; return self(); } /** * Adds an issue location. * * @param location The location. * @return This builder. */ public B addLocation(Location location) { Checks.isNotNull(location, LOCATION); this.locations.add(location); return self(); } /** * Sets the issue location. * * @param location The location. * @return This builder. */ public B location(Location location) { Checks.isNotNull(location, LOCATION); this.locations.clear(); this.locations.add(location); return self(); } /** * Sets the issue location. * * @param item The {@link LocatedItem} whose location is used. * @return This builder. */ public B location(LocatedItem item) { return location(item.getLocation()); } /** * Sets the issue locations. * * @param locations The locations * @return This builder. */ public B locations(Location... locations) { Checks.isNotNull(locations, LOCATIONS); this.locations.clear(); Collections.addAll(this.locations, locations); return self(); } /** * Sets the issue locations. * * @param locations The locations * @return This builder. */ public B locations(List locations) { Checks.isNotNull(locations, LOCATIONS); this.locations.clear(); this.locations.addAll(locations); return self(); } /** * Sets the issue snapshot. * * @param snapshot The snapshot identifier. * @return This builder. */ public B snapshot(String snapshot) { this.snapshot = snapshot; return self(); } /** * Sets the issue severity. * * @param severity The severity. * @return This builder. */ public B severity(IssueSeverity severity) { Checks.isNotNull(severity, SEVERITY); this.severity = severity; return self(); } /** * Sets the issue description. * * @param description The description. * @return This builder. */ public B description(String description) { Checks.isNotNull(description, DESCRIPTION); this.description = description; return self(); } /** * Sets the issue description. *

* The passed builder is built to a {@link StructuredDescription} which * is then converted to String. * * @param description The description. * @return This builder. */ public B description(StructuredDescription.Builder description) { Checks.isNotNull(description, DESCRIPTION); this.description = description.build().toString(); return self(); } /** * Sets the issue description. *

* {@link Object#toString()} is used. * * @param description The description. * @return This builder. */ public B description(Object description) { Checks.isNotNull(description, DESCRIPTION); return description(description.toString()); } /** * Sets the issue meta data. * * @param metas The meta data. * @return This builder. * @deprecated Use {@link Builder#metas(Metas)}. */ @Deprecated(since = "2023-03-30", forRemoval = true) public B metas(Params metas) { Checks.isNotNull(metas, METAS); this.metas = Metas.of(metas); return self(); } public B metas(Metas metas) { Checks.isNotNull(metas, METAS); this.metas = metas; return self(); } public Issue build() { return new Issue(this); } } }