DefaultComponent.java

/*
 * Copyright © 2025 Christian Grobmeier, Piotr P. Karwasz
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.github.sbom.enforcer.support;

import com.github.packageurl.PackageURL;
import io.github.sbom.enforcer.Component;
import io.github.sbom.enforcer.internal.Artifacts;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.aether.artifact.Artifact;
import org.jspecify.annotations.Nullable;

/**
 * Default {@link Component} implementation.
 */
public final class DefaultComponent implements Component {

    private final Artifact artifact;
    private final @Nullable PackageURL purl;
    private final Set<Artifact> billsOfMaterials;
    private final Set<ExternalReference> externalReferences;
    private final Map<ChecksumAlgorithm, String> checksums;

    public static Builder newBuilder() {
        return new Builder();
    }

    private DefaultComponent(
            Artifact artifact,
            @Nullable PackageURL purl,
            Set<Artifact> billsOfMaterials,
            Set<ExternalReference> externalReferences,
            Map<ChecksumAlgorithm, String> checksums) {
        this.artifact = artifact;
        this.purl = purl;
        this.billsOfMaterials = Collections.unmodifiableSet(billsOfMaterials);
        this.externalReferences = Collections.unmodifiableSet(externalReferences);
        this.checksums = Collections.unmodifiableMap(checksums);
    }

    @Override
    public Artifact getArtifact() {
        return artifact;
    }

    @Override
    public @Nullable PackageURL getPurl() {
        return purl;
    }

    @Override
    public Collection<Artifact> getBillsOfMaterials() {
        return billsOfMaterials;
    }

    @Override
    public Collection<ExternalReference> getExternalReferences() {
        return externalReferences;
    }

    @Override
    public Map<ChecksumAlgorithm, String> getChecksums() {
        return checksums;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof DefaultComponent that)) return false;
        return artifact.equals(that.artifact)
                && Objects.equals(purl, that.purl)
                && billsOfMaterials.equals(that.billsOfMaterials)
                && externalReferences.equals(that.externalReferences)
                && checksums.equals(that.checksums);
    }

    @Override
    public int hashCode() {
        return Objects.hash(artifact, purl, billsOfMaterials, externalReferences, checksums);
    }

    public static final class Builder {
        private @Nullable Artifact artifact;
        private @Nullable PackageURL purl;
        private final Set<Artifact> billsOfMaterials = new TreeSet<>(Artifacts::compare);
        private final Set<ExternalReference> externalReferences = new TreeSet<>(DefaultExternalReference::compare);
        private final Map<ChecksumAlgorithm, String> checksums = new EnumMap<>(ChecksumAlgorithm.class);

        private Builder() {}

        public Builder setArtifact(Artifact artifact) {
            this.artifact = artifact;
            return this;
        }

        public Builder setPurl(PackageURL purl) {
            this.purl = purl;
            return this;
        }

        public Builder addBillOfMaterials(Artifact artifact) {
            this.billsOfMaterials.add(artifact);
            return this;
        }

        public Builder addExternalReference(String referenceType, String location) {
            this.externalReferences.add(DefaultExternalReference.of(referenceType, location));
            return this;
        }

        public Builder addChecksum(ChecksumAlgorithm checksumAlgorithm, String value) {
            this.checksums.put(checksumAlgorithm, value);
            return this;
        }

        public Component get() {
            if (artifact == null) {
                throw new IllegalStateException("Required artifact object was not provided.");
            }
            return new DefaultComponent(artifact, purl, billsOfMaterials, externalReferences, checksums);
        }
    }
}