diff --git a/pom.xml b/pom.xml
index 8923361..d6b3b75 100644
--- a/pom.xml
+++ b/pom.xml
@@ -132,10 +132,10 @@
2.38.0
0.8.13
- 2.58.0
- 4.9.3.0
- 2.45.0
- 4.9.3
+ 2.83.0
+ 4.9.8.2
+ 3.1.0
+ 4.9.8
3.1.1
1.37
diff --git a/src/main/java/com/github/packageurl/PackageURL.java b/src/main/java/com/github/packageurl/PackageURL.java
index a474651..2530f7c 100644
--- a/src/main/java/com/github/packageurl/PackageURL.java
+++ b/src/main/java/com/github/packageurl/PackageURL.java
@@ -908,6 +908,10 @@ public static final class StandardTypes {
@Deprecated
public static final String NIXPKGS = "nix";
+ public static final String BAZEL = "bazel";
+
+ public static final String JULIA = "julia";
+
private StandardTypes() {}
}
}
diff --git a/src/main/java/com/github/packageurl/PackageURLBuilders.java b/src/main/java/com/github/packageurl/PackageURLBuilders.java
new file mode 100644
index 0000000..05a3020
--- /dev/null
+++ b/src/main/java/com/github/packageurl/PackageURLBuilders.java
@@ -0,0 +1,4079 @@
+/*
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.github.packageurl;
+
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.TreeMap;
+import org.jspecify.annotations.Nullable;
+
+/**
+ * Builder factories for creating type-safe Package URLs (PURLs).
+ *
+ * Each method returns a builder enforcing correct fields and allowed qualifiers
+ * for that PURL type based on the official
+ * PURL specification.
+ *
+ * @since 2.0.0
+ */
+public final class PackageURLBuilders {
+ private PackageURLBuilders() {}
+
+ /**
+ * Returns a builder for Arch Linux packages and other users of the libalpm/pacman package manager.
+ *
+ * @return a new AlpmBuilder
+ */
+ public static AlpmBuilder alpm() {
+ return new AlpmBuilder();
+ }
+
+ /**
+ * Returns an alpine Linux APK packages.
+ *
+ * @return a new ApkBuilder
+ */
+ public static ApkBuilder apk() {
+ return new ApkBuilder();
+ }
+
+ /**
+ * Returns a builder for Bazel modules as specified in
+ * Bazel modules.
+ *
+ * @return a new BazelBuilder
+ */
+ public static BazelBuilder bazel() {
+ return new BazelBuilder();
+ }
+
+ /**
+ * Returns a builder for Bitbucket packages.
+ *
+ * @return a new BitbucketBuilder
+ */
+ public static BitbucketBuilder bitbucket() {
+ return new BitbucketBuilder();
+ }
+
+ /**
+ * Returns a builder for Bitnami packages.
+ *
+ * @return a new BitnamiBuilder
+ */
+ public static BitnamiBuilder bitnami() {
+ return new BitnamiBuilder();
+ }
+
+ /**
+ * Returns a builder for Cargo packages for Rust.
+ *
+ * @return a new CargoBuilder
+ */
+ public static CargoBuilder cargo() {
+ return new CargoBuilder();
+ }
+
+ /**
+ * Returns a builder for CocoaPods pods.
+ *
+ * @return a new CocoapodsBuilder
+ */
+ public static CocoapodsBuilder cocoapods() {
+ return new CocoapodsBuilder();
+ }
+
+ /**
+ * Returns a builder for Composer PHP packages.
+ *
+ * @return a new ComposerBuilder
+ */
+ public static ComposerBuilder composer() {
+ return new ComposerBuilder();
+ }
+
+ /**
+ * Returns a builder for Conan C/C++ packages.
+ *
+ * The PURL is designed to closely resemble the Conan-native {@code /@/}
+ * syntax for package references as specified in
+ * Package terminology.
+ *
+ * @return a new ConanBuilder
+ */
+ public static ConanBuilder conan() {
+ return new ConanBuilder();
+ }
+
+ /**
+ * Returns a builder for Conda packages.
+ *
+ * @return a new CondaBuilder
+ */
+ public static CondaBuilder conda() {
+ return new CondaBuilder();
+ }
+
+ /**
+ * Returns a builder for perl package distributions published on CPAN.
+ *
+ * @return a new CpanBuilder
+ */
+ public static CpanBuilder cpan() {
+ return new CpanBuilder();
+ }
+
+ /**
+ * Returns a build for CRAN R packages.
+ *
+ * @return a new CranBuilder
+ */
+ public static CranBuilder cran() {
+ return new CranBuilder();
+ }
+
+ /**
+ * Returns a builder for debian packages, Debian derivatives, and Ubuntu packages.
+ *
+ * @return a new DebBuilder
+ */
+ public static DebBuilder deb() {
+ return new DebBuilder();
+ }
+
+ /**
+ * Returns a builder for Docker images.
+ *
+ * @return a new DockerBuilder
+ */
+ public static DockerBuilder docker() {
+ return new DockerBuilder();
+ }
+
+ /**
+ * Returns a builder for RubyGems.
+ *
+ * @return a new GemBuilder
+ */
+ public static GemBuilder gem() {
+ return new GemBuilder();
+ }
+
+ /**
+ * Returns the builder for plain, generic packages that do not fit anywhere
+ * else such as for "upstream-from-distro" packages. In
+ * particular this is handy for a plain version control repository such as
+ * a bare git repo in combination with a {@code vcs_url}.
+ *
+ * @return a new GenericBuilder
+ */
+ public static GenericBuilder generic() {
+ return new GenericBuilder();
+ }
+
+ /**
+ * Returns a builder for GitHub packages.
+ *
+ * @return a new GithubBuilder
+ */
+ public static GithubBuilder github() {
+ return new GithubBuilder();
+ }
+
+ /**
+ * Returns a builder Go packages.
+ *
+ * @return a new GolangBuilder
+ */
+ public static GolangBuilder golang() {
+ return new GolangBuilder();
+ }
+
+ /**
+ * Returns a builder Haskell packages.
+ *
+ * @return a new HackageBuilder
+ */
+ public static HackageBuilder hackage() {
+ return new HackageBuilder();
+ }
+
+ /**
+ * Returns a builder for Hex packages.
+ *
+ * @return a new HexBuilder
+ */
+ public static HexBuilder hex() {
+ return new HexBuilder();
+ }
+
+ /**
+ * Returns a builder for Hugging Face ML models.
+ *
+ * @return a new HuggingfaceBuilder
+ */
+ public static HuggingfaceBuilder huggingface() {
+ return new HuggingfaceBuilder();
+ }
+
+ /**
+ * Returns a builder for Julia packages
+ *
+ * @return a new JuliaBuilder
+ */
+ public static JuliaBuilder julia() {
+ return new JuliaBuilder();
+ }
+
+ /**
+ * Returns a builder for Lua packages installed with LuaRocks.
+ *
+ * @return a new LuarocksBuilder
+ */
+ public static LuarocksBuilder luarocks() {
+ return new LuarocksBuilder();
+ }
+
+ /**
+ * Returns a builder for Maven JARs and related artifacts.
+ *
+ * @return a new MavenBuilder
+ */
+ public static MavenBuilder maven() {
+ return new MavenBuilder();
+ }
+
+ /**
+ * Returns a builder for MLflow ML models (Azure ML, Databricks, etc.)
+ *
+ * @return a new MlflowBuilder
+ */
+ public static MlflowBuilder mlflow() {
+ return new MlflowBuilder();
+ }
+
+ /**
+ * Returns a builder for NPM packages.
+ *
+ * @return a new NpmBuilder
+ */
+ public static NpmBuilder npm() {
+ return new NpmBuilder();
+ }
+
+ /**
+ * Returns a builder for NuGet .NET packages.
+ *
+ * @return a new NugetBuilder
+ */
+ public static NugetBuilder nuget() {
+ return new NugetBuilder();
+ }
+
+ /**
+ * Returns a builder for artifacts stored in registries that conform to the
+ * OCI Distribution Specification
+ * including container images built by Docker and others.
+ *
+ * @return a new OciBuilder
+ */
+ public static OciBuilder oci() {
+ return new OciBuilder();
+ }
+
+ /**
+ * Returns a builder for Dart and Flutter pub packages.
+ *
+ * @return a new PubBuilder
+ */
+ public static PubBuilder pub() {
+ return new PubBuilder();
+ }
+
+ /**
+ * Returns a builder for Python packages.
+ *
+ * a python packages
+ *
+ * @return a new PypiBuilder
+ */
+ public static PypiBuilder pypi() {
+ return new PypiBuilder();
+ }
+
+ /**
+ * Returns a QNX packages.
+ *
+ * @return a new QpkgBuilder
+ */
+ public static QpkgBuilder qpkg() {
+ return new QpkgBuilder();
+ }
+
+ /**
+ * Returns an RPM packages.
+ *
+ * @return a new RpmBuilder
+ */
+ public static RpmBuilder rpm() {
+ return new RpmBuilder();
+ }
+
+ /**
+ * Returns a builder for ISO-IEC 19770-2 Software Identification (SWID) tags.
+ *
+ * @return a new SwidBuilder
+ */
+ public static SwidBuilder swid() {
+ return new SwidBuilder();
+ }
+
+ /**
+ * Returns a builder for Swift packages.
+ *
+ * @return a new SwiftBuilder
+ */
+ public static SwiftBuilder swift() {
+ return new SwiftBuilder();
+ }
+
+ /**
+ * Common builder shared between the different types.
+ *
+ * @param the builder type
+ */
+ public abstract static class Builder> {
+ protected final Map qualifiers = new TreeMap<>();
+
+ protected @Nullable String subpath;
+
+ protected Builder() {}
+
+ /**
+ * Sets the subpath.
+ *
+ * @param subpath the subpath
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withSubpath(String subpath) {
+ this.subpath = subpath;
+ return (T) this;
+ }
+
+ /**
+ * Removes the subpath
+ *
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withoutSubpath() {
+ this.subpath = null;
+ return (T) this;
+ }
+
+ /**
+ * Allows the specification of a version range. The value must adhere to the Version Range Specification.
+ *
+ * @param vers the value
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withVers(String vers) {
+ qualifiers.put("vers", vers);
+ return (T) this;
+ }
+
+ /**
+ * Removes the vers.
+ *
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withoutVers() {
+ qualifiers.remove("vers");
+ return (T) this;
+ }
+
+ /**
+ * An extra URL for an alternative, non-default package repository or registry.
+ *
+ * @param repositoryUrl the repository URL
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withRepositoryUrl(String repositoryUrl) {
+ qualifiers.put("repository_url", repositoryUrl);
+ return (T) this;
+ }
+
+ /**
+ * Removes the repository URL.
+ *
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withoutRepositoryUrl() {
+ qualifiers.remove("repository_url");
+ return (T) this;
+ }
+
+ /**
+ * An extra URL for a direct package web download URL to optionally qualify a PURL.
+ *
+ * @param downloadUrl the value
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withDownloadUrl(String downloadUrl) {
+ qualifiers.put("download_url", downloadUrl);
+ return (T) this;
+ }
+
+ /**
+ * Removes the download URL.
+ *
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withoutDownloadUrl() {
+ qualifiers.remove("download_url");
+ return (T) this;
+ }
+
+ /**
+ * An extra URL for a package version control system URL to optionally qualify a PURL.
+ *
+ * @param vcsUrl the value
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withVcsUrl(String vcsUrl) {
+ qualifiers.put("vcs_url", vcsUrl);
+ return (T) this;
+ }
+
+ /**
+ * Removes the VCS URL.
+ *
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withoutVcsUrl() {
+ qualifiers.remove("vcs_url");
+ return (T) this;
+ }
+
+ /**
+ * An extra file name of a package archive.
+ *
+ * @param fileName the value
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withFileName(String fileName) {
+ qualifiers.put("file_name", fileName);
+ return (T) this;
+ }
+
+ /**
+ * Removes the file name.
+ *
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withoutFileName() {
+ qualifiers.remove("file_name");
+ return (T) this;
+ }
+
+ /**
+ * A qualifier for one or more checksums stored as a comma-separated list.
+ *
+ * @param checksum the value
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withChecksum(String checksum) {
+ qualifiers.put("checksum", checksum);
+ return (T) this;
+ }
+
+ /**
+ * Removes the checksum
+ *
+ * @return this builder instance
+ */
+ @SuppressWarnings("unchecked")
+ public T withoutChecksum() {
+ qualifiers.remove("checksum");
+ return (T) this;
+ }
+
+ /**
+ * Returns empty. This builder has no default repository.
+ *
+ * @return empty
+ */
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.empty();
+ }
+
+ /**
+ * Constructs the final Package URL.
+ *
+ * @return a fully constructed Package URL
+ * @throws MalformedPackageURLException if the Package URL is invalid
+ */
+ public abstract PackageURL build() throws MalformedPackageURLException;
+ }
+
+ /**
+ * Builder for Arch Linux packages and other users of the libalpm/pacman package manager.
+ */
+ public static final class AlpmBuilder extends Builder {
+ private @Nullable String vendor;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private AlpmBuilder() {}
+
+ /**
+ * Sets the vendor such as arch, arch32, archarm, manjaro or msys.
+ *
+ * @param vendor the vendor value
+ * @return this builder instance
+ */
+ public AlpmBuilder withVendor(String vendor) {
+ this.vendor = vendor;
+ return this;
+ }
+
+ /**
+ * Removes the namespace.
+ *
+ * @return this builder instance
+ */
+ public AlpmBuilder withoutVendor() {
+ this.vendor = null;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public AlpmBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public AlpmBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public AlpmBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the package architecture.
+ *
+ * @param arch the package architecture
+ * @return this builder instance
+ */
+ public AlpmBuilder withArch(String arch) {
+ this.qualifiers.put("arch", arch);
+ return this;
+ }
+
+ /**
+ * Removes the package architecture.
+ *
+ * @return this builder instance
+ */
+ public AlpmBuilder withoutArch() {
+ this.qualifiers.remove("arch");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ Objects.requireNonNull(this.vendor);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.ALPM)
+ .withNamespace(this.vendor)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Alpine Linux APK-based packages.
+ */
+ public static final class ApkBuilder extends Builder {
+ private @Nullable String vendor;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private ApkBuilder() {}
+
+ /**
+ * Sets the vendor such as alpine or openwrt. It is not case-sensitive and must be lowercased.
+ *
+ * @param vendor the vendor such as alpine or openwrt
+ * @return this builder instance
+ */
+ public ApkBuilder withVendor(String vendor) {
+ this.vendor = vendor;
+ return this;
+ }
+
+ /**
+ * Removes the vendor.
+ *
+ * @return this builder instance
+ */
+ public ApkBuilder withoutVendor() {
+ this.vendor = null;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public ApkBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public ApkBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public ApkBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the package architecture.
+ *
+ * @param arch the package architecture
+ * @return this builder instance
+ */
+ public ApkBuilder withArch(String arch) {
+ this.qualifiers.put("arch", arch);
+ return this;
+ }
+
+ /**
+ * Removes the package architecture.
+ *
+ * @return this builder instance
+ */
+ public ApkBuilder withoutArch() {
+ this.qualifiers.remove("arch");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ Objects.requireNonNull(this.vendor);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.APK)
+ .withNamespace(this.vendor)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Bazel modules as specified in
+ * Bazel modules.
+ */
+ public static final class BazelBuilder extends Builder {
+ /**
+ * The default repository is the Bazel Central Registry (BCR).
+ */
+ public static final String DEFAULT_REPOSITORY_URL = "https://bcr.bazel.build";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private @Nullable String label;
+
+ private BazelBuilder() {}
+
+ /**
+ * Sets the name as defined in the {@code MODULE.bazel} file.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public BazelBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public BazelBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public BazelBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the subpath.
+ *
+ * @param subpath the subpath
+ * @return this builder instance
+ * @deprecated use {@link #withLabel(String)}
+ */
+ @Override
+ @Deprecated
+ public BazelBuilder withSubpath(String subpath) {
+ return withLabel(subpath);
+ }
+
+ /**
+ * Removes the subpath.
+ *
+ * @return this builder instance
+ * @deprecated use {@link #withoutLabel()}
+ */
+ @Override
+ @Deprecated
+ public BazelBuilder withoutSubpath() {
+ return withoutLabel();
+ }
+
+ /**
+ * Sets the optional subpath which MAY refer to a label of a particular
+ * package or target in the module
+ * (Labels). The
+ * label MUST NOT include a repo name and the leading {@code '//'} MUST
+ * be omitted. When referring to targets, the label MUST include the
+ * name of the target, separated from the package by {@code ':'}. If
+ * there is no target name, subpath is assumed to refer to the whole
+ * package.
+ *
+ * @param label the optional subpath
+ * @return this builder instance
+ */
+ public BazelBuilder withLabel(String label) {
+ this.label = label;
+ return this;
+ }
+
+ /**
+ * Removes the optional subpath.
+ *
+ * @return this builder instance
+ */
+ public BazelBuilder withoutLabel() {
+ this.label = null;
+ return this;
+ }
+
+ /**
+ * The URL of the registry that hosts this Bazel module. If not specified, it defaults to the
+ * {@link #DEFAULT_REPOSITORY_URL BCR URL}.
+ *
+ * @param registry the URL of the registry that hosts this Bazel module
+ * @return this builder instance
+ */
+ public BazelBuilder withRegistry(String registry) {
+ this.qualifiers.put("repository_url", registry);
+ return this;
+ }
+
+ /**
+ * Removes the URL of the registry that hosts this Bazel module.
+ *
+ * @return this builder instance
+ */
+ public BazelBuilder withoutRegistry() {
+ this.qualifiers.remove("repository_url");
+ return this;
+ }
+
+ /**
+ * Returns the Bazel Central Registry (BCR).
+ *
+ * @return the Bazel Central Registry (BCR)
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ Objects.requireNonNull(this.version);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.BAZEL)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.label)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Bitbucket packages.
+ */
+ public static final class BitbucketBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://bitbucket.org";
+
+ private @Nullable String organization;
+
+ private @Nullable String name;
+
+ private @Nullable String commit;
+
+ private BitbucketBuilder() {}
+
+ /**
+ * Sets the user or organization.
+ *
+ * @param organization the user or organization
+ * @return this builder instance
+ */
+ public BitbucketBuilder withOrganization(String organization) {
+ this.organization = organization;
+ return this;
+ }
+
+ /**
+ * Removes the user or organization.
+ *
+ * @return this builder instance
+ */
+ public BitbucketBuilder withoutOrganization() {
+ this.organization = null;
+ return this;
+ }
+
+ /**
+ * Sets the repository name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public BitbucketBuilder withRepositoryName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the commit or tag.
+ *
+ * @param commit the commit or tag
+ * @return this builder instance
+ */
+ public BitbucketBuilder withCommit(String commit) {
+ this.commit = commit;
+ return this;
+ }
+
+ /**
+ * Removes the commit or tag.
+ *
+ * @return this builder instance
+ */
+ public BitbucketBuilder withoutCommit() {
+ this.commit = null;
+ return this;
+ }
+
+ /**
+ * Returns the default Bitbucket repository URL.
+ *
+ * @return the default Bitbucket repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ Objects.requireNonNull(this.organization);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.BITBUCKET)
+ .withNamespace(this.organization)
+ .withName(this.name)
+ .withVersion(this.commit)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Bitnami packages.
+ */
+ public static final class BitnamiBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://downloads.bitnami.com/files/stacksmith";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private BitnamiBuilder() {}
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public BitnamiBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the full package version, including version and revision.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public BitnamiBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the full package version.
+ *
+ * @return this builder instance
+ */
+ public BitnamiBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the package architecture. Available values are amd64 (default) and arm64.
+ *
+ * @param arch the package architecture
+ * @return this builder instance
+ */
+ public BitnamiBuilder withArch(String arch) {
+ this.qualifiers.put("arch", arch);
+ return this;
+ }
+
+ /**
+ * Removes the package architecture.
+ *
+ * @return this builder instance
+ */
+ public BitnamiBuilder withoutArch() {
+ this.qualifiers.remove("arch");
+ return this;
+ }
+
+ /**
+ * Sets the distribution associated with the package.
+ *
+ * @param distro the distribution associated with the package
+ * @return this builder instance
+ */
+ public BitnamiBuilder withDistro(String distro) {
+ this.qualifiers.put("distro", distro);
+ return this;
+ }
+
+ /**
+ * Removes the distribution associated with the package.
+ *
+ * @return this builder instance
+ */
+ public BitnamiBuilder withoutDistro() {
+ this.qualifiers.remove("distro");
+ return this;
+ }
+
+ /**
+ * Returns the default Bitnami repository URL.
+ *
+ * @return the default Bitnami repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.BITNAMI)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Cargo packages for Rust.
+ */
+ public static final class CargoBuilder extends Builder {
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private CargoBuilder() {}
+
+ /**
+ * Sets the repository name.
+ *
+ * @param name the repository name
+ * @return this builder instance
+ */
+ public CargoBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the package version.
+ *
+ * @param version the package version
+ * @return this builder instance
+ */
+ public CargoBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public CargoBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.CARGO)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for CocoaPods pods.
+ */
+ public static final class CocoapodsBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://cdn.cocoapods.org/";
+
+ private @Nullable String name;
+
+ private @Nullable String packageVersion;
+
+ private CocoapodsBuilder() {}
+
+ /**
+ * Sets the pod name.
+ *
+ * @param name the pod name
+ * @return this builder instance
+ */
+ public CocoapodsBuilder withPodName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the package version.
+ *
+ * @param packageVersion the version string
+ * @return this builder instance
+ */
+ public CocoapodsBuilder withPackageVersion(String packageVersion) {
+ this.packageVersion = packageVersion;
+ return this;
+ }
+
+ /**
+ * Removes the package version.
+ *
+ * @return this builder instance
+ */
+ public CocoapodsBuilder withoutVersion() {
+ this.packageVersion = null;
+ return this;
+ }
+
+ /**
+ * Sets the pods subspec.
+ *
+ * @param subpath the subpath
+ * @return this builder instance
+ */
+ @Override
+ public CocoapodsBuilder withSubpath(String subpath) {
+ this.subpath = subpath;
+ return this;
+ }
+
+ /**
+ * Removes the pods subspec.
+ *
+ * @return this builder instance
+ */
+ @Override
+ public CocoapodsBuilder withoutSubpath() {
+ this.subpath = null;
+ return this;
+ }
+
+ /**
+ * Returns the default CocoaPods repository URL.
+ *
+ * @return the default CocoaPods repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.COCOAPODS)
+ .withName(this.name)
+ .withVersion(this.packageVersion)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Composer PHP packages
+ */
+ public static final class ComposerBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://packagist.org";
+
+ private @Nullable String vendor;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private ComposerBuilder() {}
+
+ /**
+ * Sets the namespace.
+ *
+ * @param vendor the namespace
+ * @return this builder instance
+ */
+ public ComposerBuilder withVendor(String vendor) {
+ this.vendor = vendor;
+ return this;
+ }
+
+ /**
+ * Removes the namespace.
+ *
+ * @return this builder instance
+ */
+ public ComposerBuilder withoutVendor() {
+ this.vendor = null;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public ComposerBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public ComposerBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public ComposerBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Returns the default Composer repository URL.
+ *
+ * @return the default Composer repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ Objects.requireNonNull(this.vendor);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.COMPOSER)
+ .withNamespace(this.vendor)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Conan C/C++ packages. The purl is designed to closely resemble the
+ * Conan-native {@code /@/} syntax
+ * for package references as specified in
+ * Package terminology.
+ */
+ public static final class ConanBuilder extends Builder {
+ private @Nullable String vendor;
+
+ private @Nullable String name;
+
+ private @Nullable String packageVersion;
+
+ private ConanBuilder() {}
+
+ /**
+ * Sets the vendor of the package.
+ *
+ * @param vendor the vendor of the package
+ * @return this builder instance
+ */
+ public ConanBuilder withVendor(String vendor) {
+ this.vendor = vendor;
+ return this;
+ }
+
+ /**
+ * Removes the vendor of the package.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutVendor() {
+ this.vendor = null;
+ return this;
+ }
+
+ /**
+ * Sets the Conan {@code }.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public ConanBuilder withPackageName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the Conan {@code }.
+ *
+ * @param packageVersion the version string
+ * @return this builder instance
+ */
+ public ConanBuilder withPackageVersion(String packageVersion) {
+ this.packageVersion = packageVersion;
+ return this;
+ }
+
+ /**
+ * Removes the Conan {@code }.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutPackageVersion() {
+ this.packageVersion = null;
+ return this;
+ }
+
+ /**
+ * Sets the Conan {@code }. Only required if the Conan package was published with {@code }.
+ *
+ * @param user the Conan {@code }
+ * @return this builder instance
+ */
+ public ConanBuilder withUser(String user) {
+ this.qualifiers.put("user", user);
+ return this;
+ }
+
+ /**
+ * Remove the Conan {@code }.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutUser() {
+ this.qualifiers.remove("user");
+ return this;
+ }
+
+ /**
+ * Sets the Conan {@code }. Only required if the Conan package was published with Conan {@code }.
+ *
+ * @param channel the Conan {@code }
+ * @return this builder instance
+ */
+ public ConanBuilder withChannel(String channel) {
+ this.qualifiers.put("channel", channel);
+ return this;
+ }
+
+ /**
+ * Removes the Conan {@code }.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutChannel() {
+ this.qualifiers.remove("channel");
+ return this;
+ }
+
+ /**
+ * Sets the Conan recipe revision (optional). If omitted, the PURL
+ * refers to the latest recipe revision available for the given version.
+ *
+ * @param recipleRevision the Conan recipe revision
+ * @return this builder instance
+ */
+ public ConanBuilder withRecipeRevision(String recipleRevision) {
+ this.qualifiers.put("rrev", recipleRevision);
+ return this;
+ }
+
+ /**
+ * Removes the Conan recipe revision.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutRecipeRevision() {
+ this.qualifiers.remove("rrev");
+ return this;
+ }
+
+ /**
+ * Sets the Conan package revision (optional). If omitted, the PURL
+ * refers to the latest package revision available for the given version and recipe revision.
+ *
+ * @param packageRevision the Conan package revision
+ * @return this builder instance
+ */
+ public ConanBuilder withPackageRevision(String packageRevision) {
+ this.qualifiers.put("prev", packageRevision);
+ return this;
+ }
+
+ /**
+ * Removes the Conan package revision.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutPackageRevision() {
+ this.qualifiers.remove("prev");
+ return this;
+ }
+
+ /**
+ * Sets the Conan package architecture.
+ *
+ * @param arch the architecture string
+ * @return this builder instance
+ */
+ public ConanBuilder withArch(String arch) {
+ this.qualifiers.put("arch", arch);
+ return this;
+ }
+
+ /**
+ * Removes the Conan package architecture.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutArch() {
+ this.qualifiers.remove("arch");
+ return this;
+ }
+
+ /**
+ * Sets the Conan build type.
+ *
+ * @param buildType the build type
+ * @return this builder instance
+ */
+ public ConanBuilder withBuildType(String buildType) {
+ this.qualifiers.put("build_type", buildType);
+ return this;
+ }
+
+ /**
+ * Removes the Conan build type.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutBuildType() {
+ this.qualifiers.remove("build_type");
+ return this;
+ }
+
+ /**
+ * Sets the Conan compiler.
+ *
+ * @param compiler the compiler name
+ * @return this builder instance
+ */
+ public ConanBuilder withCompiler(String compiler) {
+ this.qualifiers.put("compiler", compiler);
+ return this;
+ }
+
+ /**
+ * Removes the Conan compiler.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutCompiler() {
+ this.qualifiers.remove("compiler");
+ return this;
+ }
+
+ /**
+ * Sets the Conan compiler runtime.
+ *
+ * @param compilerRuntime the compiler runtime string
+ * @return this builder instance
+ */
+ public ConanBuilder withCompilerRuntime(String compilerRuntime) {
+ this.qualifiers.put("compiler.runtime", compilerRuntime);
+ return this;
+ }
+
+ /**
+ * Removes the Conan compiler runtime.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutCompilerRuntime() {
+ this.qualifiers.remove("compiler.runtime");
+ return this;
+ }
+
+ /**
+ * Sets the Conan compiler version.
+ *
+ * @param compilerVersion the compiler version
+ * @return this builder instance
+ */
+ public ConanBuilder withCompilerVersion(String compilerVersion) {
+ this.qualifiers.put("compiler.version", compilerVersion);
+ return this;
+ }
+
+ /**
+ * Removes the Conan compiler version.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutCompilerVersion() {
+ this.qualifiers.remove("compiler.version");
+ return this;
+ }
+
+ /**
+ * Sets the Conan operating system.
+ *
+ * @param os the operating system string
+ * @return this builder instance
+ */
+ public ConanBuilder withOs(String os) {
+ this.qualifiers.put("os", os);
+ return this;
+ }
+
+ /**
+ * Removes the Conan operating system.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutOs() {
+ this.qualifiers.remove("os");
+ return this;
+ }
+
+ /**
+ * Sets whether the Conan package is shared.
+ *
+ * @param shared {@code "True"} if shared or {@code "False"} if not
+ * @return this builder instance
+ */
+ public ConanBuilder withShared(String shared) {
+ this.qualifiers.put("shared", shared);
+ return this;
+ }
+
+ /**
+ * Removes the shared value.
+ *
+ * @return this builder instance
+ */
+ public ConanBuilder withoutShared() {
+ this.qualifiers.remove("shared");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.CONAN)
+ .withNamespace(this.vendor)
+ .withName(this.name)
+ .withVersion(this.packageVersion)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Conda packages.
+ */
+ public static final class CondaBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://repo.anaconda.com";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private CondaBuilder() {}
+
+ /**
+ * Sets the package name.
+ *
+ * @param name the package name
+ * @return this builder instance
+ */
+ public CondaBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the package version.
+ *
+ * @param version the package version
+ * @return this builder instance
+ */
+ public CondaBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the package version.
+ *
+ * @return this builder instance
+ */
+ public CondaBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the build string.
+ *
+ * @param build the build string
+ * @return this builder instance
+ */
+ public CondaBuilder withBuild(String build) {
+ this.qualifiers.put("build", build);
+ return this;
+ }
+
+ /**
+ * Removes the build string.
+ *
+ * @return this builder instance
+ */
+ public CondaBuilder withoutBuild() {
+ this.qualifiers.remove("build");
+ return this;
+ }
+
+ /**
+ * Sets the package stored location.
+ *
+ * @param channel the package stored location
+ * @return this builder instance
+ */
+ public CondaBuilder withChannel(String channel) {
+ this.qualifiers.put("channel", channel);
+ return this;
+ }
+
+ /**
+ * Removes the package stored location.
+ *
+ * @return this builder instance
+ */
+ public CondaBuilder withoutChannel() {
+ this.qualifiers.remove("channel");
+ return this;
+ }
+
+ /**
+ * Sets the associated platform.
+ *
+ * @param subdir the associated platform
+ * @return this builder instance
+ */
+ public CondaBuilder withSubdir(String subdir) {
+ this.qualifiers.put("subdir", subdir);
+ return this;
+ }
+
+ /**
+ * Removes the associated platform.
+ *
+ * @return this builder instance
+ */
+ public CondaBuilder withoutSubdir() {
+ this.qualifiers.remove("subdir");
+ return this;
+ }
+
+ /**
+ * Sets the package type.
+ *
+ * @param packageType the package type
+ * @return this builder instance
+ */
+ public CondaBuilder withPackageType(String packageType) {
+ this.qualifiers.put("type", packageType);
+ return this;
+ }
+
+ /**
+ * Removes the package type.
+ *
+ * @return this builder instance
+ */
+ public CondaBuilder withoutPackageType() {
+ this.qualifiers.remove("type");
+ return this;
+ }
+
+ /**
+ * Returns the default Conda repository URL.
+ *
+ * @return the default Conda repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.CONDA)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Perl package distributions published on CPAN.
+ */
+ public static final class CpanBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://www.cpan.org/";
+
+ private @Nullable String cpanId;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private CpanBuilder() {}
+
+ /**
+ * Sets the namespace.
+ *
+ * @param cpanId the namespace
+ * @return this builder instance
+ */
+ public CpanBuilder withCpanId(String cpanId) {
+ this.cpanId = cpanId;
+ return this;
+ }
+
+ /**
+ * Removes the namespace.
+ *
+ * @return this builder instance
+ */
+ public CpanBuilder withoutCpanId() {
+ this.cpanId = null;
+ return this;
+ }
+
+ /**
+ * Sets the distribution name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public CpanBuilder withDistributionName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public CpanBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public CpanBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Returns the default CPAN repository URL.
+ *
+ * @return the default CPAN repository URL.
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ Objects.requireNonNull(this.cpanId);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.CPAN)
+ .withNamespace(this.cpanId)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for CRAN R packages.
+ */
+ public static final class CranBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://cran.r-project.org";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private CranBuilder() {}
+
+ /**
+ * Sets the package name.
+ *
+ * @param name the package name
+ * @return this builder instance
+ */
+ public CranBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the package version.
+ *
+ * @param version the package version
+ * @return this builder instance
+ */
+ public CranBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public CranBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Returns the default CRAN repository URL.
+ *
+ * @return the default CRAN repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.CRAN)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Debian packages, Debian derivatives, and Ubuntu packages.
+ */
+ public static final class DebBuilder extends Builder {
+ private @Nullable String vendor;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private DebBuilder() {}
+
+ /**
+ * Sets the "vendor" name such as "debian" or "ubuntu".
+ *
+ * @param vendor the vendor
+ * @return this builder instance
+ */
+ public DebBuilder withVendor(String vendor) {
+ this.vendor = vendor;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public DebBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version of the binary (or source) package.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public DebBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version of the binary (or source) package.
+ *
+ * @return this builder instance
+ */
+ public DebBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the package architecture.
+ *
+ * @param arch the package architecture
+ * @return this builder instance
+ */
+ public DebBuilder withArch(String arch) {
+ this.qualifiers.put("arch", arch);
+ return this;
+ }
+
+ /**
+ * Removes the package architecture.
+ *
+ * @return this builder instance
+ */
+ public DebBuilder withoutArch() {
+ this.qualifiers.remove("arch");
+ return this;
+ }
+
+ /**
+ * Sets the distribution associated with the package.
+ *
+ * @param distro the distribution associated with the package
+ * @return this builder instance
+ */
+ public DebBuilder withDistro(String distro) {
+ this.qualifiers.put("distro", distro);
+ return this;
+ }
+
+ /**
+ * Removes the distribution associated with the package.
+ *
+ * @return this builder instance
+ */
+ public DebBuilder withoutDistro() {
+ this.qualifiers.remove("distro");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ Objects.requireNonNull(this.vendor);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.DEB)
+ .withNamespace(this.vendor)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Docker images.
+ */
+ public static final class DockerBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://hub.docker.com";
+
+ private @Nullable String registry;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private DockerBuilder() {}
+
+ /**
+ * Sets the registry/user/organization.
+ *
+ * @param registry the namespace
+ * @return this builder instance
+ */
+ public DockerBuilder withRegistry(String registry) {
+ this.registry = registry;
+ return this;
+ }
+
+ /**
+ * Removes the registry/user/organization.
+ *
+ * @return this builder instance
+ */
+ public DockerBuilder withoutRegistry() {
+ this.registry = null;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public DockerBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the sha256 image id or tag. Since tags can be moved, a sha256 image id is preferred.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public DockerBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the sha256 image id or tag.
+ *
+ * @return this builder instance
+ */
+ public DockerBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Returns the default Docker repository URL.
+ *
+ * @return the default Docker repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.DOCKER)
+ .withNamespace(this.registry)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for RubyGems.
+ */
+ public static final class GemBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://rubygems.org";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private GemBuilder() {}
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public GemBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public GemBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public GemBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets an alternative platform, such as java for JRuby. The implied default is ruby for Ruby MRI.
+ *
+ * @param platform the platform string
+ * @return this builder instance
+ */
+ public GemBuilder withPlatform(String platform) {
+ this.qualifiers.put("platform", platform);
+ return this;
+ }
+
+ /**
+ * Removes the platform.
+ *
+ * @return this builder instance
+ */
+ public GemBuilder withoutPlatform() {
+ this.qualifiers.remove("platform");
+ return this;
+ }
+
+ /**
+ * Returns the default RubyGems repository URL.
+ *
+ * @return the default RubyGems repository URL.
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.GEM)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * The generic type is for plain, generic packages that do not fit anywhere
+ * else such as for "upstream-from-distro" packages. In
+ * particular this is handy for a plain version control
+ * repository such as a bare git repo in combination with a {@code vcs_url}.
+ */
+ public static final class GenericBuilder extends Builder {
+ private @Nullable String namespace;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private GenericBuilder() {}
+
+ /**
+ * Sets the namespace.
+ *
+ * @param namespace the namespace
+ * @return this builder instance
+ */
+ public GenericBuilder withNamespace(String namespace) {
+ this.namespace = namespace;
+ return this;
+ }
+
+ /**
+ * Removes the namespace.
+ *
+ * @return this builder instance
+ */
+ public GenericBuilder withoutNamespace() {
+ this.namespace = null;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public GenericBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version string
+ * @return this builder instance
+ */
+ public GenericBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public GenericBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.GENERIC)
+ .withNamespace(this.namespace)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for GitHub packages.
+ */
+ public static final class GithubBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://github.com";
+
+ private @Nullable String organization;
+
+ private @Nullable String repositoryName;
+
+ private @Nullable String commit;
+
+ private GithubBuilder() {}
+
+ /**
+ * Sets the namespace.
+ *
+ * @param organization the namespace
+ * @return this builder instance
+ */
+ public GithubBuilder withOrganization(String organization) {
+ this.organization = organization;
+ return this;
+ }
+
+ /**
+ * Removes the namespace.
+ *
+ * @return this builder instance
+ */
+ public GithubBuilder withoutOrganization() {
+ this.organization = null;
+ return this;
+ }
+
+ /**
+ * Sets the repository name.
+ *
+ * @param repositoryName the repository name
+ * @return this builder instance
+ */
+ public GithubBuilder withRepositoryName(String repositoryName) {
+ this.repositoryName = repositoryName;
+ return this;
+ }
+
+ /**
+ * Sets the commit or tag.
+ *
+ * @param commit the commit or tag
+ * @return this builder instance
+ */
+ public GithubBuilder withCommit(String commit) {
+ this.commit = commit;
+ return this;
+ }
+
+ /**
+ * Removes the commit or tag.
+ *
+ * @return this builder instance
+ */
+ public GithubBuilder withoutCommit() {
+ this.commit = null;
+ return this;
+ }
+
+ /**
+ * Gets the default GitHub repository URL.
+ *
+ * @return the default GitHub repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.repositoryName);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.GITHUB)
+ .withNamespace(this.organization)
+ .withName(this.repositoryName)
+ .withVersion(this.commit)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Go packages.
+ */
+ public static final class GolangBuilder extends Builder {
+ private @Nullable String namespace;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private GolangBuilder() {}
+
+ /**
+ * Sets the namespace.
+ *
+ * @param namespace the namespace
+ * @return this builder instance
+ */
+ public GolangBuilder withNamespace(String namespace) {
+ this.namespace = namespace;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public GolangBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public GolangBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public GolangBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.namespace);
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.GOLANG)
+ .withNamespace(this.namespace)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Haskell packages.
+ */
+ public static final class HackageBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://hackage.haskell.org";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private HackageBuilder() {}
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public HackageBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the package version.
+ *
+ * @param version the package version
+ * @return this builder instance
+ */
+ public HackageBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the package version.
+ *
+ * @return this builder instance
+ */
+ public HackageBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Gets the default Haskell repository URL.
+ *
+ * @return the default Haskell repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.HACKAGE)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Hex packages.
+ */
+ public static final class HexBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://repo.hex.pm";
+
+ private @Nullable String organization;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private HexBuilder() {}
+
+ /**
+ * Sets the organization for private packages.
+ *
+ * @param organization the organization for private packages
+ * @return this builder instance
+ */
+ public HexBuilder withOrganization(String organization) {
+ this.organization = organization;
+ return this;
+ }
+
+ /**
+ * Removes the organization.
+ *
+ * @return this builder instance
+ */
+ public HexBuilder withoutOrganization() {
+ this.organization = null;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public HexBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public HexBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public HexBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Gets the default Hex repository URL.
+ *
+ * @return the default Hex repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.HEX)
+ .withNamespace(this.organization)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Hugging Face ML models.
+ */
+ public static final class HuggingfaceBuilder extends Builder {
+ private @Nullable String organization;
+
+ private @Nullable String name;
+
+ private @Nullable String commit;
+
+ private HuggingfaceBuilder() {}
+
+ /**
+ * Sets the model repository username or organization.
+ *
+ * @param namespace the model repository username or organization
+ * @return this builder instance
+ */
+ public HuggingfaceBuilder withOrganization(String namespace) {
+ this.organization = namespace;
+ return this;
+ }
+
+ /**
+ * Sets the model repository name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public HuggingfaceBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the model revision Git commit hash.
+ *
+ * @param commit model revision Git commit hash
+ * @return this builder instance
+ */
+ public HuggingfaceBuilder withCommit(String commit) {
+ this.commit = commit;
+ return this;
+ }
+
+ /**
+ * Removes the commit.
+ *
+ * @return this builder instance
+ */
+ public HuggingfaceBuilder withoutCommit() {
+ this.commit = null;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.HUGGINGFACE)
+ .withNamespace(this.organization)
+ .withName(this.name)
+ .withVersion(this.commit)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Julia packages.
+ */
+ public static final class JuliaBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://github.com/JuliaRegistries/General";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private JuliaBuilder() {}
+
+ /**
+ * Sets the package name (without a `.jl` suffix).
+ *
+ * @param name the package name
+ * @return this builder instance
+ */
+ public JuliaBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public JuliaBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public JuliaBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the Julia package UUID.
+ * @param uuid the Julia package UUID
+ *
+ * @return this builder instance
+ */
+ public JuliaBuilder withUuid(String uuid) {
+ this.qualifiers.put("uuid", uuid);
+ return this;
+ }
+
+ /**
+ * Gets the default Julia repository URL.
+ *
+ * @return the default Julia repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.JULIA)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Lua packages installed with LuaRocks.
+ */
+ public static final class LuarocksBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://luarocks.org";
+
+ private @Nullable String manifest;
+
+ private @Nullable String name;
+
+ private @Nullable String versionRevision;
+
+ private LuarocksBuilder() {}
+
+ /**
+ * Sets the user manifest under which the package is registered.
+ *
+ * @param manifest the user manifest
+ * @return this builder instance
+ */
+ public LuarocksBuilder withManifest(String manifest) {
+ this.manifest = manifest;
+ return this;
+ }
+
+ /**
+ * Removes the user manifest.
+ *
+ * @return this builder instance
+ */
+ public LuarocksBuilder withoutManifest() {
+ this.manifest = null;
+ return this;
+ }
+
+ /**
+ * Sets the LuaRocks package name.
+ *
+ * @param name the LuaRocks package name
+ * @return this builder instance
+ */
+ public LuarocksBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the full package version, including module version and rockspec revision.
+ *
+ * @param versionRevision the full package version
+ * @return this builder instance
+ */
+ public LuarocksBuilder withVersionRevision(String versionRevision) {
+ this.versionRevision = versionRevision;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public LuarocksBuilder withoutVersionRevision() {
+ this.versionRevision = null;
+ return this;
+ }
+
+ /**
+ * Sets the LuaRocks rocks server to be used; useful in case a private
+ * server is used (optional). If omitted,
+ * https://luarocks.org as the
+ * default server is assumed.
+ *
+ * @param repositoryUrl the repository URL
+ * @return this builder instance
+ */
+ @Override
+ public LuarocksBuilder withRepositoryUrl(String repositoryUrl) {
+ qualifiers.put("repository_url", repositoryUrl);
+ return this;
+ }
+
+ /**
+ * Gets the default Luarocks repository URL.
+ *
+ * @return the default Luarocks repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.LUAROCKS)
+ .withNamespace(this.manifest)
+ .withName(this.name)
+ .withVersion(this.versionRevision)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Maven JARs and related artifacts.
+ */
+ public static final class MavenBuilder extends Builder {
+ /**
+ * The Maven Central repository is the public repository for Apache
+ * Maven packages. This repository is also mirrored at
+ *
+ * https://repo1.maven.org/maven2/. Use the standard
+ * {@code repository_url} qualifier to point to another repository.
+ */
+ public static final String DEFAULT_REPOSITORY_URL = "https://repo.maven.apache.org/maven2/";
+
+ private @Nullable String groupId;
+
+ private @Nullable String artifactId;
+
+ private @Nullable String version;
+
+ private MavenBuilder() {}
+
+ /**
+ * Sets the group identifier.
+ *
+ * @param groupId the group identifier
+ * @return this builder instance
+ */
+ public MavenBuilder withGroupId(String groupId) {
+ this.groupId = groupId;
+ return this;
+ }
+
+ /**
+ * Removes the group identifier.
+ *
+ * @return this builder instance
+ */
+ public MavenBuilder withoutGroupId() {
+ this.groupId = null;
+ return this;
+ }
+
+ /**
+ * Sets the artifact identifier.
+ *
+ * @param artifactId the artifact identifier
+ * @return this builder instance
+ */
+ public MavenBuilder withArtifactId(String artifactId) {
+ this.artifactId = artifactId;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public MavenBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public MavenBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the Maven classifier as defined in the POM documentation.
+ *
+ * @param classifier the classifier
+ * @return this builder instance
+ */
+ public MavenBuilder withClassifier(String classifier) {
+ this.qualifiers.put("classifier", classifier);
+ return this;
+ }
+
+ /**
+ * Removes the classifier.
+ *
+ * @return this builder instance
+ */
+ public MavenBuilder withoutClassifier() {
+ this.qualifiers.remove("classifier");
+ return this;
+ }
+
+ /**
+ * Sets the Maven type as defined in the POM documentation. Note that
+ * Maven uses a concept/coordinate called packaging which does not map
+ * directly 1:1 to a file extension. In this use case, we need to
+ * construct a link to one of many possible artifacts. Maven itself
+ * uses type in a dependency declaration when needed to disambiguate between them.
+ *
+ * @param type the type
+ * @return this builder instance
+ */
+ public MavenBuilder withType(String type) {
+ this.qualifiers.put("type", type);
+ return this;
+ }
+
+ /**
+ * Removes the type.
+ *
+ * @return this builder instance
+ */
+ public MavenBuilder withoutType() {
+ this.qualifiers.remove("type");
+ return this;
+ }
+
+ /**
+ * Gets the default Maven repository URL.
+ *
+ * @return the default Maven repository URL.
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.groupId);
+ Objects.requireNonNull(this.artifactId);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.MAVEN)
+ .withNamespace(this.groupId)
+ .withName(this.artifactId)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for MLflow ML models (Azure ML, Databricks, etc.).
+ */
+ public static final class MlflowBuilder extends Builder {
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private MlflowBuilder() {}
+
+ /**
+ * Sets the model name.
+ *
+ * @param name the model name
+ * @return this builder instance
+ */
+ public MlflowBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public MlflowBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public MlflowBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the {@code model_uuid} as defined in the MLflow documentation.
+ *
+ * @param modelUuid the {@code model_uuid}
+ * @return this builder instance
+ */
+ public MlflowBuilder withModelUuid(String modelUuid) {
+ this.qualifiers.put("model_uuid", modelUuid);
+ return this;
+ }
+
+ /**
+ * Removes the {@code model_uuid}.
+ * @return this builder instance
+ */
+ public MlflowBuilder withoutModelUuid() {
+ this.qualifiers.remove("model_uuid");
+ return this;
+ }
+
+ /**
+ * Sets the {@code run_id} as defined in the MLflow documentation.
+ *
+ * @param runId the {@code run_id}
+ * @return this builder instance
+ */
+ public MlflowBuilder withRunId(String runId) {
+ this.qualifiers.put("run_id", runId);
+ return this;
+ }
+
+ /**
+ * Removes the {@code run_id}.
+ *
+ * @return this builder instance
+ */
+ public MlflowBuilder withoutRunId() {
+ this.qualifiers.remove("run_id");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.MLFLOW)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for NPM packages.
+ */
+ public static final class NpmBuilder extends Builder {
+ /**
+ * The default repository is the npm Registry at
+ * https://registry.npmjs.org.
+ */
+ public static final String DEFAULT_REPOSITORY_URL = "https://registry.npmjs.org/";
+
+ private @Nullable String scope;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private NpmBuilder() {}
+
+ /**
+ * Sets the scope of a scoped NPM package.
+ *
+ * @param scope the scope of a scoped NPM package
+ * @return this builder instance
+ */
+ public NpmBuilder withScope(String scope) {
+ this.scope = scope;
+ return this;
+ }
+
+ /**
+ * Removes the scope.
+ *
+ * @return this builder instance
+ */
+ public NpmBuilder withoutScope() {
+ this.scope = null;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public NpmBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public NpmBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public NpmBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Gets the default NPM repository URL.
+ *
+ * @return the default NPM repository URL
+ */
+ @Override
+ public Optional getDefaultRepositoryUrl() {
+ return Optional.of(DEFAULT_REPOSITORY_URL);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.NPM)
+ .withNamespace(this.scope)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for NuGet .NET packages.
+ */
+ public static final class NugetBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://www.nuget.org";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private NugetBuilder() {}
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public NugetBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public NugetBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public NugetBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.NUGET)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for artifacts stored in registries that conform to the OCI Distribution
+ * Specification
+ * .https://github.com/opencontainers/distribution-spec
+ * including container images built by Docker and others.
+ */
+ public static final class OciBuilder extends Builder {
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private OciBuilder() {}
+
+ /**
+ * Sets the name.
+ *
+ * The name is the last fragment of the repository name. For example if
+ * the repository name is {@code library/debian} then the name is {@code debian}.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public OciBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public OciBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public OciBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Sets the package architecture.
+ *
+ * @param arch the package architecture
+ * @return this builder instance
+ */
+ public OciBuilder withArch(String arch) {
+ this.qualifiers.put("arch", arch);
+ return this;
+ }
+
+ /**
+ * Removes the package architecture.
+ *
+ * @return this builder instance
+ */
+ public OciBuilder withoutArch() {
+ this.qualifiers.remove("arch");
+ return this;
+ }
+
+ /**
+ * Sets the artifact tag that may have been associated with the digest at the time.
+ *
+ * @param tag the artifact tag
+ * @return this builder instance
+ */
+ public OciBuilder withTag(String tag) {
+ this.qualifiers.put("tag", tag);
+ return this;
+ }
+
+ /**
+ * Removes the package tag.
+ *
+ * @return this builder instance
+ */
+ public OciBuilder withoutTag() {
+ this.qualifiers.remove("tag");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.OCI)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Dart and Flutter pub packages.
+ */
+ public static final class PubBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://pub.dartlang.org";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private PubBuilder() {}
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public PubBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public PubBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public PubBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.PUB)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Python packages.
+ */
+ public static final class PypiBuilder extends Builder {
+ public static final String DEFAULT_REPOSITORY_URL = "https://pypi.org";
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private PypiBuilder() {}
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public PypiBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public PypiBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public PypiBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * Selects a particular distribution file (case-sensitive).
+ *
+ * For naming convention, see the Python Packaging User Guide on
+ * source distributions
+ * and on
+ * binary distributions
+ * and the rules for
+ * platform compatibility tags.
+ *
+ * @param fileName the distribution file (case-sensitive)
+ * @return this builder instance
+ */
+ @Override
+ public PypiBuilder withFileName(String fileName) {
+ this.qualifiers.put("file_name", fileName);
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public PypiBuilder withoutFileName() {
+ this.qualifiers.remove("file_name");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.PYPI)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for QNX packages.
+ */
+ public static final class QpkgBuilder extends Builder {
+ private @Nullable String vendor;
+
+ private @Nullable String name;
+
+ private @Nullable String version;
+
+ private QpkgBuilder() {}
+
+ /**
+ * Sets the vendor of the package.
+ *
+ * @param vendor the vendor of the package
+ * @return this builder instance
+ */
+ public QpkgBuilder withVendor(String vendor) {
+ this.vendor = vendor;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public QpkgBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version.
+ *
+ * @param version the version
+ * @return this builder instance
+ */
+ public QpkgBuilder withVersion(String version) {
+ this.version = version;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public QpkgBuilder withoutVersion() {
+ this.version = null;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.QPKG)
+ .withNamespace(this.vendor)
+ .withName(this.name)
+ .withVersion(this.version)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for RPM packages.
+ */
+ public static final class RpmBuilder extends Builder {
+ private @Nullable String vendor;
+
+ private @Nullable String name;
+
+ private @Nullable String versionRelease;
+
+ private RpmBuilder() {}
+
+ /**
+ * Sets the vendor such as Fedora or OpenSUSE.
+ *
+ * @param vendor the vendor
+ * @return this builder instance
+ */
+ public RpmBuilder withVendor(String vendor) {
+ this.vendor = vendor;
+ return this;
+ }
+
+ /**
+ * Sets the name.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public RpmBuilder withName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * Sets the version-release.
+ *
+ * @param versionRelease the version-release
+ * @return this builder instance
+ */
+ public RpmBuilder withVersionRelease(String versionRelease) {
+ this.versionRelease = versionRelease;
+ return this;
+ }
+
+ /**
+ * Removes the version-release.
+ *
+ * @return this builder instance
+ */
+ public RpmBuilder withoutVersionRelease() {
+ this.versionRelease = null;
+ return this;
+ }
+
+ /**
+ * Sets the package epoch.
+ *
+ * @param epoch the package epoch
+ * @return this builder instance
+ */
+ public RpmBuilder withEpoch(String epoch) {
+ this.qualifiers.put("epoch", epoch);
+ return this;
+ }
+
+ /**
+ * Removes the package epoch.
+ *
+ * @return this builder instance
+ */
+ public RpmBuilder withoutEpoch() {
+ this.qualifiers.remove("epoch");
+ return this;
+ }
+
+ /**
+ * Sets the package architecture.
+ *
+ * @param arch the package architecture
+ * @return this builder instance
+ */
+ public RpmBuilder withArch(String arch) {
+ this.qualifiers.put("arch", arch);
+ return this;
+ }
+
+ /**
+ * Removes the package architecture.
+ *
+ * @return this builder instance
+ */
+ public RpmBuilder withoutArch() {
+ this.qualifiers.remove("arch");
+ return this;
+ }
+
+ /**
+ * Sets the distribution associated with the package.
+ *
+ * @param distro the distribution associated with the package
+ * @return this builder instance
+ */
+ public RpmBuilder withDistro(String distro) {
+ this.qualifiers.put("distro", distro);
+ return this;
+ }
+
+ /**
+ * Removes the distribution associated with the package.
+ *
+ * @return this builder instance
+ */
+ public RpmBuilder withoutDistro() {
+ this.qualifiers.remove("distro");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.vendor);
+ Objects.requireNonNull(this.name);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.RPM)
+ .withNamespace(this.vendor)
+ .withName(this.name)
+ .withVersion(this.versionRelease)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for ISO-IEC 19770-2 Software Identification (SWID) tags.
+ */
+ public static final class SwidBuilder extends Builder {
+ private @Nullable String softwareCreator;
+
+ private @Nullable String softwareIdentityName;
+
+ private @Nullable String softwareIdentityVersion;
+
+ private SwidBuilder() {}
+
+ /**
+ * Sets the optional name and regid of the entity with a role of {@code softwareCreator}.
+ *
+ * @param softwareCreator name and regid
+ * @return this builder instance
+ */
+ public SwidBuilder withSoftwareCreator(String softwareCreator) {
+ this.softwareCreator = softwareCreator;
+ return this;
+ }
+
+ /**
+ * Removes the name and regid of the entity with a role of {@code softwareCreator}.
+ *
+ * @return this builder instance
+ */
+ public SwidBuilder withoutSoftwareCreator() {
+ this.softwareCreator = null;
+ return this;
+ }
+
+ /**
+ * Sets the name as defined in the SWID {@code SoftwareIdentity} element.
+ *
+ * @param name the name
+ * @return this builder instance
+ */
+ public SwidBuilder withSoftwareIdentityName(String name) {
+ this.softwareIdentityName = name;
+ return this;
+ }
+
+ /**
+ * Sets the version as defined in the SWID {@code SoftwareIdentity} element.
+ *
+ * @param softwareIdentityVersion the version
+ * @return this builder instance
+ */
+ public SwidBuilder withSoftwareIdentityVersion(String softwareIdentityVersion) {
+ this.softwareIdentityVersion = softwareIdentityVersion;
+ return this;
+ }
+
+ /**
+ * Removes the version.
+ *
+ * @return this builder instance
+ */
+ public SwidBuilder withoutSoftwareIdentityVersion() {
+ this.softwareIdentityVersion = null;
+ return this;
+ }
+
+ /**
+ * Sets the {@code tagId} as defined in the SWID
+ * {@code SoftwareIdentity} element. Per the SWID specification, GUIDs
+ * are recommended.
+ *
+ * @param tagId the {@code tagId}
+ * @return this builder instance
+ */
+ public SwidBuilder withTagId(String tagId) {
+ this.qualifiers.put("tag_id", tagId);
+ return this;
+ }
+
+ /**
+ * Removes the {@code tagId}.
+ *
+ * @return this builder instance
+ */
+ public SwidBuilder withoutTagId() {
+ this.qualifiers.remove("tag_id");
+ return this;
+ }
+
+ /**
+ * Sets the {@code tagVersion} as defined in the SWID
+ * {@code SoftwareIdentity} element. Per the SWID specification, GUIDs
+ * are recommended.
+ *
+ * @param tagVersion the {@code tagVersion}
+ * @return this builder instance
+ */
+ public SwidBuilder withTagVersion(String tagVersion) {
+ this.qualifiers.put("tag_version", tagVersion);
+ return this;
+ }
+
+ /**
+ * Removes the {@code tagVersion}.
+ *
+ * @return this builder instance
+ */
+ public SwidBuilder withoutTagVersion() {
+ this.qualifiers.remove("tag_version");
+ return this;
+ }
+
+ /**
+ * Sets the {@code patch} as defined in the SWID {@code
+ * SoftwareIdentity} element.
+ *
+ * @param patch {@code the patch}
+ * @return this builder instance
+ */
+ public SwidBuilder withPatch(String patch) {
+ this.qualifiers.put("patch", patch);
+ return this;
+ }
+
+ /**
+ * Removes the {@code patch}.
+ *
+ * @return this builder instance
+ */
+ public SwidBuilder withoutPatch() {
+ this.qualifiers.remove("patch");
+ return this;
+ }
+
+ /**
+ * Sets the {@code tagCreatorName} as defined in the SWID
+ * {@code SoftwareIdentity} element.
+ *
+ * @param tagCreatorName the {@code tagCreatorName}
+ * @return this builder instance
+ */
+ public SwidBuilder withTagCreatorName(String tagCreatorName) {
+ this.qualifiers.put("tag_creator_name", tagCreatorName);
+ return this;
+ }
+
+ /**
+ * Removes the {@code tagCreatorName}.
+ *
+ * @return this builder instance
+ */
+ public SwidBuilder withoutTagCreatorName() {
+ this.qualifiers.remove("tag_creator_name");
+ return this;
+ }
+
+ /**
+ * Sets the {@code tagCreatorRegid} as defined in the SWID
+ * {@code SoftwareIdentity} element.
+ *
+ * @param tagCreatorRegid the {@code tagCreatorRegid}
+ * @return this builder instance
+ */
+ public SwidBuilder withTagCreatorRegid(String tagCreatorRegid) {
+ this.qualifiers.put("tag_creator_regid", tagCreatorRegid);
+ return this;
+ }
+
+ /**
+ * Removes the {@code tagCreatorRegid}.
+ *
+ * @return this builder instance
+ */
+ public SwidBuilder withoutTagCreatorRegid() {
+ this.qualifiers.remove("tag_creator_regid");
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.softwareIdentityName);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.SWID)
+ .withNamespace(this.softwareCreator)
+ .withName(this.softwareIdentityName)
+ .withVersion(this.softwareIdentityVersion)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+
+ /**
+ * Builder for Swift packages.
+ */
+ public static final class SwiftBuilder extends Builder {
+ private @Nullable String namespace;
+
+ private @Nullable String repositoryName;
+
+ private @Nullable String packageVersion;
+
+ private SwiftBuilder() {}
+
+ /**
+ * Sets the source host and user/organization.
+ *
+ * @param namespace the source host and user/organization
+ * @return this builder instance
+ */
+ public SwiftBuilder withNamespace(String namespace) {
+ this.namespace = namespace;
+ return this;
+ }
+
+ /**
+ * Sets the repository repositoryName.
+ *
+ * @param repositoryName the repository repositoryName
+ * @return this builder instance
+ */
+ public SwiftBuilder withRepositoryName(String repositoryName) {
+ this.repositoryName = repositoryName;
+ return this;
+ }
+
+ /**
+ * Sets the package version.
+ *
+ * @param packageVersion the package version
+ * @return this builder instance
+ */
+ public SwiftBuilder withPackageVersion(String packageVersion) {
+ this.packageVersion = packageVersion;
+ return this;
+ }
+
+ /**
+ * Removes the package version.
+ *
+ * @return this builder instance
+ */
+ public SwiftBuilder withoutPackageVersion() {
+ this.packageVersion = null;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageURL build() throws MalformedPackageURLException {
+ Objects.requireNonNull(this.namespace);
+ Objects.requireNonNull(this.repositoryName);
+ return PackageURLBuilder.aPackageURL()
+ .withType(PackageURL.StandardTypes.SWIFT)
+ .withNamespace(this.namespace)
+ .withName(this.repositoryName)
+ .withVersion(this.packageVersion)
+ .withQualifiers(!this.qualifiers.isEmpty() ? this.qualifiers : null)
+ .withSubpath(this.subpath)
+ .build();
+ }
+ }
+}
diff --git a/src/test/java/com/github/packageurl/PackageURLBuildersTest.java b/src/test/java/com/github/packageurl/PackageURLBuildersTest.java
new file mode 100644
index 0000000..b5d8da1
--- /dev/null
+++ b/src/test/java/com/github/packageurl/PackageURLBuildersTest.java
@@ -0,0 +1,981 @@
+/*
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package com.github.packageurl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+public final class PackageURLBuildersTest {
+ @Test
+ void testAlpm() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:alpm/arch/pacman@6.0.1-1?arch=x86_64")
+ .build(),
+ PackageURLBuilders.alpm()
+ .withVendor("arch")
+ .withName("pacman")
+ .withVersion("6.0.1-1")
+ .withArch("x86_64")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:alpm/arch/python-pip@21.0-1?arch=any")
+ .build(),
+ PackageURLBuilders.alpm()
+ .withVendor("arch")
+ .withName("python-pip")
+ .withVersion("21.0-1")
+ .withArch("any")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:alpm/arch/containers-common@1:0.47.4-4?arch=x86_64")
+ .build(),
+ PackageURLBuilders.alpm()
+ .withVendor("arch")
+ .withName("containers-common")
+ .withVersion("1:0.47.4-4")
+ .withArch("x86_64")
+ .build());
+ }
+
+ @Test
+ void testApk() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:apk/alpine/curl@7.83.0-r0?arch=x86")
+ .build(),
+ PackageURLBuilders.apk()
+ .withVendor("alpine")
+ .withName("curl")
+ .withVersion("7.83.0-r0")
+ .withArch("x86")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:apk/alpine/apk@2.12.9-r3?arch=x86")
+ .build(),
+ PackageURLBuilders.apk()
+ .withVendor("alpine")
+ .withName("apk")
+ .withVersion("2.12.9-r3")
+ .withArch("x86")
+ .build());
+ }
+
+ @Test
+ void testBazel() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bazel/rules_java@7.8.0").build(),
+ PackageURLBuilders.bazel()
+ .withName("rules_java")
+ .withVersion("7.8.0")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bazel/curl@8.8.0.bcr.1").build(),
+ PackageURLBuilders.bazel()
+ .withName("curl")
+ .withVersion("8.8.0.bcr.1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bazel/curl@8.8.0?repository_url=https://example.org/bazel-registry")
+ .build(),
+ PackageURLBuilders.bazel()
+ .withName("curl")
+ .withVersion("8.8.0")
+ .withRepositoryUrl("https://example.org/bazel-registry")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bazel/rules_java@8.5.0#java/runfiles")
+ .build(),
+ PackageURLBuilders.bazel()
+ .withName("rules_java")
+ .withVersion("8.5.0")
+ .withLabel("java/runfiles")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bazel/rules_java@8.5.0#java/runfiles:runfiles")
+ .build(),
+ PackageURLBuilders.bazel()
+ .withName("rules_java")
+ .withVersion("8.5.0")
+ .withLabel("java/runfiles:runfiles")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bazel/rules_go@0.48.0#go").build(),
+ PackageURLBuilders.bazel()
+ .withName("rules_go")
+ .withVersion("0.48.0")
+ .withLabel("go")
+ .build());
+ }
+
+ @Test
+ void testBitbucket() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bitbucket/birkenfeld/pygments-main@244fd47e07d1014f0aed9c")
+ .build(),
+ PackageURLBuilders.bitbucket()
+ .withOrganization("birkenfeld")
+ .withRepositoryName("pygments-main")
+ .withCommit("244fd47e07d1014f0aed9c")
+ .build());
+ }
+
+ @Test
+ void testBitnami() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bitnami/wordpress?distro=debian-12")
+ .build(),
+ PackageURLBuilders.bitnami()
+ .withName("wordpress")
+ .withDistro("debian-12")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bitnami/wordpress@6.2.0?distro=debian-12")
+ .build(),
+ PackageURLBuilders.bitnami()
+ .withName("wordpress")
+ .withVersion("6.2.0")
+ .withDistro("debian-12")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bitnami/wordpress@6.2.0?arch=arm64&distro=debian-12")
+ .build(),
+ PackageURLBuilders.bitnami()
+ .withName("wordpress")
+ .withVersion("6.2.0")
+ .withArch("arm64")
+ .withDistro("debian-12")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:bitnami/wordpress@6.2.0?arch=arm64&distro=photon-4")
+ .build(),
+ PackageURLBuilders.bitnami()
+ .withName("wordpress")
+ .withVersion("6.2.0")
+ .withArch("arm64")
+ .withDistro("photon-4")
+ .build());
+ }
+
+ @Test
+ void testCargo() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cargo/rand@0.7.2").build(),
+ PackageURLBuilders.cargo().withName("rand").withVersion("0.7.2").build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cargo/clap@2.33.0").build(),
+ PackageURLBuilders.cargo()
+ .withName("clap")
+ .withVersion("2.33.0")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cargo/structopt@0.3.11").build(),
+ PackageURLBuilders.cargo()
+ .withName("structopt")
+ .withVersion("0.3.11")
+ .build());
+ }
+
+ @Test
+ void testCocoapods() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cocoapods/AFNetworking@4.0.1")
+ .build(),
+ PackageURLBuilders.cocoapods()
+ .withPodName("AFNetworking")
+ .withPackageVersion("4.0.1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cocoapods/MapsIndoors@3.24.0")
+ .build(),
+ PackageURLBuilders.cocoapods()
+ .withPodName("MapsIndoors")
+ .withPackageVersion("3.24.0")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cocoapods/ShareKit@2.0#Twitter")
+ .build(),
+ PackageURLBuilders.cocoapods()
+ .withPodName("ShareKit")
+ .withPackageVersion("2.0")
+ .withSubpath("Twitter")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cocoapods/GoogleUtilities@7.5.2#NSData+zlib")
+ .build(),
+ PackageURLBuilders.cocoapods()
+ .withPodName("GoogleUtilities")
+ .withPackageVersion("7.5.2")
+ .withSubpath("NSData+zlib")
+ .build());
+ }
+
+ @Test
+ void testComposer() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:composer/laravel/laravel@5.5.0")
+ .build(),
+ PackageURLBuilders.composer()
+ .withVendor("laravel")
+ .withName("laravel")
+ .withVersion("5.5.0")
+ .build());
+ }
+
+ @Test
+ void testConan() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:conan/openssl@3.0.3").build(),
+ PackageURLBuilders.conan()
+ .withPackageName("openssl")
+ .withPackageVersion("3.0.3")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:conan/openssl.org/openssl@3.0.3?user=bincrafters&channel=stable")
+ .build(),
+ PackageURLBuilders.conan()
+ .withVendor("openssl.org")
+ .withPackageName("openssl")
+ .withPackageVersion("3.0.3")
+ .withUser("bincrafters")
+ .withChannel("stable")
+ .build());
+ // FIXME: Definition missing these qualifiers
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:conan/openssl.org/openssl@3.0.3?arch=x86_64&build_type=Debug&compiler=Visual%20Studio&compiler.runtime=MDd&compiler.version=16&os=Windows&shared=True&rrev=93a82349c31917d2d674d22065c7a9ef9f380c8e&prev=b429db8a0e324114c25ec387bfd8281f330d7c5c")
+ .build(),
+ PackageURLBuilders.conan()
+ .withVendor("openssl.org")
+ .withPackageName("openssl")
+ .withPackageVersion("3.0.3")
+ .withArch("x86_64")
+ .withBuildType("Debug")
+ .withCompiler("Visual Studio")
+ .withCompilerRuntime("MDd")
+ .withCompilerVersion("16")
+ .withOs("Windows")
+ .withShared("True")
+ .withRecipeRevision("93a82349c31917d2d674d22065c7a9ef9f380c8e")
+ .withPackageRevision("b429db8a0e324114c25ec387bfd8281f330d7c5c")
+ .build());
+ }
+
+ @Test
+ void testConda() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:conda/absl-py@0.4.1?build=py36h06a4308_0&channel=main&subdir=linux-64&type=tar.bz2")
+ .build(),
+ PackageURLBuilders.conda()
+ .withName("absl-py")
+ .withVersion("0.4.1")
+ .withBuild("py36h06a4308_0")
+ .withChannel("main")
+ .withSubdir("linux-64")
+ .withPackageType("tar.bz2")
+ .build());
+ }
+
+ @Test
+ void testCpan() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cpan/GDT/URI-PackageURL").build(),
+ PackageURLBuilders.cpan()
+ .withCpanId("GDT")
+ .withDistributionName("URI-PackageURL")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cpan/OALDERS/libwww-perl@6.76")
+ .build(),
+ PackageURLBuilders.cpan()
+ .withCpanId("OALDERS")
+ .withDistributionName("libwww-perl")
+ .withVersion("6.76")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cpan/DROLSKY/DateTime@1.55?repository_url=backpan.perl.org")
+ .build(),
+ PackageURLBuilders.cpan()
+ .withCpanId("DROLSKY")
+ .withDistributionName("DateTime")
+ .withVersion("1.55")
+ .withRepositoryUrl("backpan.perl.org")
+ .build());
+ }
+
+ @Test
+ void testCran() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cran/A3@1.0.0").build(),
+ PackageURLBuilders.cran().withName("A3").withVersion("1.0.0").build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cran/rJava@1.0-4").build(),
+ PackageURLBuilders.cran().withName("rJava").withVersion("1.0-4").build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:cran/caret@6.0-88").build(),
+ PackageURLBuilders.cran()
+ .withName("caret")
+ .withVersion("6.0-88")
+ .build());
+ }
+
+ @Test
+ void testDeb() throws MalformedPackageURLException {
+ // FIXME: Definition missing distro qualifier
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:deb/debian/curl@7.50.3-1?arch=i386&distro=jessie")
+ .build(),
+ PackageURLBuilders.deb()
+ .withVendor("debian")
+ .withName("curl")
+ .withVersion("7.50.3-1")
+ .withArch("i386")
+ .withDistro("jessie")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:deb/debian/dpkg@1.19.0.4?arch=amd64&distro=stretch")
+ .build(),
+ PackageURLBuilders.deb()
+ .withVendor("debian")
+ .withName("dpkg")
+ .withVersion("1.19.0.4")
+ .withArch("amd64")
+ .withDistro("stretch")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:deb/ubuntu/dpkg@1.19.0.4?arch=amd64")
+ .build(),
+ PackageURLBuilders.deb()
+ .withVendor("ubuntu")
+ .withName("dpkg")
+ .withVersion("1.19.0.4")
+ .withArch("amd64")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:deb/debian/attr@1:2.4.47-2?arch=source")
+ .build(),
+ PackageURLBuilders.deb()
+ .withVendor("debian")
+ .withName("attr")
+ .withVersion("1:2.4.47-2")
+ .withArch("source")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:deb/debian/attr@1:2.4.47-2%2Bb1?arch=amd64")
+ .build(),
+ PackageURLBuilders.deb()
+ .withVendor("debian")
+ .withName("attr")
+ .withVersion("1:2.4.47-2+b1")
+ .withArch("amd64")
+ .build());
+ }
+
+ @Test
+ void testDocker() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:docker/cassandra@latest").build(),
+ PackageURLBuilders.docker()
+ .withName("cassandra")
+ .withVersion("latest")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:docker/smartentry/debian@dc437cc87d10")
+ .build(),
+ PackageURLBuilders.docker()
+ .withName("debian")
+ .withVersion("dc437cc87d10")
+ .withRegistry("smartentry")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:docker/customer/dockerimage@sha256%3A244fd47e07d10?repository_url=gcr.io")
+ .build(),
+ PackageURLBuilders.docker()
+ .withName("dockerimage")
+ .withVersion("sha256:244fd47e07d10")
+ .withRegistry("customer")
+ .withRepositoryUrl("gcr.io")
+ .build());
+ }
+
+ @Test
+ void testGem() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:gem/ruby-advisory-db-check@0.12.4")
+ .build(),
+ PackageURLBuilders.gem()
+ .withName("ruby-advisory-db-check")
+ .withVersion("0.12.4")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:gem/jruby-launcher@1.1.2?platform=java")
+ .build(),
+ PackageURLBuilders.gem()
+ .withName("jruby-launcher")
+ .withVersion("1.1.2")
+ .withPlatform("java")
+ .build());
+ }
+
+ @Test
+ void testGeneric() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:generic/openssl@1.1.10g").build(),
+ PackageURLBuilders.generic()
+ .withName("openssl")
+ .withVersion("1.1.10g")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:generic/openssl@1.1.10g?download_url=https://openssl.org/source/openssl-1.1.0g.tar.gz&checksum=sha256:de4d501267da")
+ .build(),
+ PackageURLBuilders.generic()
+ .withName("openssl")
+ .withVersion("1.1.10g")
+ .withDownloadUrl("https://openssl.org/source/openssl-1.1.0g.tar.gz")
+ .withChecksum("sha256:de4d501267da")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:generic/bitwarderl?vcs_url=git%2Bhttps://git.fsfe.org/dxtr/bitwarderl%40cc55108da32")
+ .build(),
+ PackageURLBuilders.generic()
+ .withName("bitwarderl")
+ .withVcsUrl("git+https://git.fsfe.org/dxtr/bitwarderl@cc55108da32")
+ .build());
+ }
+
+ @Test
+ void testGithub() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:github/package-url/purl-spec@244fd47e07d1004")
+ .build(),
+ PackageURLBuilders.github()
+ .withOrganization("package-url")
+ .withRepositoryName("purl-spec")
+ .withCommit("244fd47e07d1004")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:github/package-url/purl-spec@244fd47e07d1004#everybody/loves/dogs")
+ .build(),
+ PackageURLBuilders.github()
+ .withOrganization("package-url")
+ .withRepositoryName("purl-spec")
+ .withCommit("244fd47e07d1004")
+ .withSubpath("everybody/loves/dogs")
+ .build());
+ }
+
+ @Test
+ void testGolang() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:golang/github.com/gorilla%2Fcontext@234fd47e07d1004f0aed9c")
+ .build(),
+ PackageURLBuilders.golang()
+ .withNamespace("github.com")
+ .withName("gorilla/context")
+ .withVersion("234fd47e07d1004f0aed9c")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:golang/google.golang.org/genproto#googleapis/api/annotations")
+ .build(),
+ PackageURLBuilders.golang()
+ .withNamespace("google.golang.org")
+ .withName("genproto")
+ .withSubpath("googleapis/api/annotations")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:golang/github.com/gorilla%2Fcontext@234fd47e07d1004f0aed9c#api")
+ .build(),
+ PackageURLBuilders.golang()
+ .withNamespace("github.com")
+ .withName("gorilla/context")
+ .withVersion("234fd47e07d1004f0aed9c")
+ .withSubpath("api")
+ .build());
+ }
+
+ @Test
+ void testHackage() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:hackage/a50@0.5").build(),
+ PackageURLBuilders.hackage().withName("a50").withVersion("0.5").build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:hackage/AC-HalfInteger@1.2.1")
+ .build(),
+ PackageURLBuilders.hackage()
+ .withName("AC-HalfInteger")
+ .withVersion("1.2.1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:hackage/3d-graphics-examples@0.0.0.2")
+ .build(),
+ PackageURLBuilders.hackage()
+ .withName("3d-graphics-examples")
+ .withVersion("0.0.0.2")
+ .build());
+ }
+
+ @Test
+ void testHex() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:hex/jason@1.1.2").build(),
+ PackageURLBuilders.hex().withName("jason").withVersion("1.1.2").build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:hex/acme/foo@2.3.").build(),
+ PackageURLBuilders.hex()
+ .withOrganization("acme")
+ .withName("foo")
+ .withVersion("2.3.")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:hex/phoenix_html@2.13.3#priv/static/phoenix_html.js")
+ .build(),
+ PackageURLBuilders.hex()
+ .withName("phoenix_html")
+ .withVersion("2.13.3")
+ .withSubpath("priv/static/phoenix_html.js")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:hex/bar@1.2.3?repository_url=https://myrepo.example.com")
+ .build(),
+ PackageURLBuilders.hex()
+ .withName("bar")
+ .withVersion("1.2.3")
+ .withRepositoryUrl("https://myrepo.example.com")
+ .build());
+ }
+
+ @Test
+ void testHuggingface() throws MalformedPackageURLException {
+ // FIXME: Example missing required organization
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:huggingface/distilbert/distilbert-base-uncased@043235d6088ecd3dd5fb5ca3592b6913fd516027")
+ .build(),
+ PackageURLBuilders.huggingface()
+ .withOrganization("distilbert")
+ .withName("distilbert-base-uncased")
+ .withCommit("043235d6088ecd3dd5fb5ca3592b6913fd516027")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:huggingface/microsoft/deberta-v3-base@559062ad13d311b87b2c455e67dcd5f1c8f65111?repository_url=https://hub-ci.huggingface.co")
+ .build(),
+ PackageURLBuilders.huggingface()
+ .withOrganization("microsoft")
+ .withName("deberta-v3-base")
+ .withCommit("559062ad13d311b87b2c455e67dcd5f1c8f65111")
+ .withRepositoryUrl("https://hub-ci.huggingface.co")
+ .build());
+ }
+
+ @Test
+ void testJulia() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:julia/Dates@1.9.0?uuid=ade2ca70-3891-5945-98fb-dc099432e06a")
+ .build(),
+ PackageURLBuilders.julia()
+ .withName("Dates")
+ .withVersion("1.9.0")
+ .withUuid("ade2ca70-3891-5945-98fb-dc099432e06a")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:julia/Dates?uuid=ade2ca70-3891-5945-98fb-dc099432e06a")
+ .build(),
+ PackageURLBuilders.julia()
+ .withName("Dates")
+ .withUuid("ade2ca70-3891-5945-98fb-dc099432e06a")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:julia/RegisterQD@0.3.1?uuid=ac24ea0c-1830-11e9-18d4-81f172323054")
+ .build(),
+ PackageURLBuilders.julia()
+ .withName("RegisterQD")
+ .withVersion("0.3.1")
+ .withUuid("ac24ea0c-1830-11e9-18d4-81f172323054")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:julia/RegisterQD@0.3.1?uuid=ac24ea0c-1830-11e9-18d4-81f172323054&repository_url=https://github.com/HolyLab/HolyLabRegistry")
+ .build(),
+ PackageURLBuilders.julia()
+ .withName("RegisterQD")
+ .withVersion("0.3.1")
+ .withUuid("ac24ea0c-1830-11e9-18d4-81f172323054")
+ .withRepositoryUrl("https://github.com/HolyLab/HolyLabRegistry")
+ .build());
+ }
+
+ @Test
+ void testLuarocks() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:luarocks/luasocket@3.1.0-1").build(),
+ PackageURLBuilders.luarocks()
+ .withName("luasocket")
+ .withVersionRevision("3.1.0-1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:luarocks/hisham/luafilesystem@1.8.0-1")
+ .build(),
+ PackageURLBuilders.luarocks()
+ .withManifest("hisham")
+ .withName("luafilesystem")
+ .withVersionRevision("1.8.0-1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:luarocks/username/packagename@0.1.0-1?repository_url=https://example.com/private_rocks_server/")
+ .build(),
+ PackageURLBuilders.luarocks()
+ .withManifest("username")
+ .withName("packagename")
+ .withVersionRevision("0.1.0-1")
+ .withRepositoryUrl("https://example.com/private_rocks_server/")
+ .build());
+ }
+
+ @Test
+ void testMaven() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1")
+ .build(),
+ PackageURLBuilders.maven()
+ .withGroupId("org.apache.xmlgraphics")
+ .withArtifactId("batik-anim")
+ .withVersion("1.9.1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=pom")
+ .build(),
+ PackageURLBuilders.maven()
+ .withGroupId("org.apache.xmlgraphics")
+ .withArtifactId("batik-anim")
+ .withVersion("1.9.1")
+ .withType("pom")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?classifier=sources")
+ .build(),
+ PackageURLBuilders.maven()
+ .withGroupId("org.apache.xmlgraphics")
+ .withArtifactId("batik-anim")
+ .withVersion("1.9.1")
+ .withClassifier("sources")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:maven/org.apache.xmlgraphics/batik-anim@1.9.1?type=zip&classifier=dist")
+ .build(),
+ PackageURLBuilders.maven()
+ .withGroupId("org.apache.xmlgraphics")
+ .withArtifactId("batik-anim")
+ .withVersion("1.9.1")
+ .withType("zip")
+ .withClassifier("dist")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:maven/net.sf.jacob-projec/jacob@1.14.3?classifier=x86&type=dll")
+ .build(),
+ PackageURLBuilders.maven()
+ .withGroupId("net.sf.jacob-projec")
+ .withArtifactId("jacob")
+ .withVersion("1.14.3")
+ .withClassifier("x86")
+ .withType("dll")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:maven/net.sf.jacob-projec/jacob@1.14.3?classifier=x64&type=dll")
+ .build(),
+ PackageURLBuilders.maven()
+ .withGroupId("net.sf.jacob-projec")
+ .withArtifactId("jacob")
+ .withVersion("1.14.3")
+ .withClassifier("x64")
+ .withType("dll")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:maven/groovy/groovy@1.0?repository_url=https://maven.google.com")
+ .build(),
+ PackageURLBuilders.maven()
+ .withGroupId("groovy")
+ .withArtifactId("groovy")
+ .withVersion("1.0")
+ .withRepositoryUrl("https://maven.google.com")
+ .build());
+ }
+
+ @Test
+ void testMlflow() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:mlflow/creditfraud@3?repository_url=https://westus2.api.azureml.ms/mlflow/v1.0/subscriptions/a50f2011-fab8-4164-af23-c62881ef8c95/resourceGroups/TestResourceGroup/providers/Microsoft.MachineLearningServices/workspaces/TestWorkspace")
+ .build(),
+ PackageURLBuilders.mlflow()
+ .withName("creditfraud")
+ .withVersion("3")
+ .withRepositoryUrl(
+ "https://westus2.api.azureml.ms/mlflow/v1.0/subscriptions/a50f2011-fab8-4164-af23-c62881ef8c95/resourceGroups/TestResourceGroup/providers/Microsoft.MachineLearningServices/workspaces/TestWorkspace")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:mlflow/trafficsigns@10?model_uuid=36233173b22f4c89b451f1228d700d49&run_id=410a3121-2709-4f88-98dd-dba0ef056b0a&repository_url=https://adb-5245952564735461.0.azuredatabricks.net/api/2.0/mlflow")
+ .build(),
+ PackageURLBuilders.mlflow()
+ .withName("trafficsigns")
+ .withVersion("10")
+ .withModelUuid("36233173b22f4c89b451f1228d700d49")
+ .withRunId("410a3121-2709-4f88-98dd-dba0ef056b0a")
+ .withRepositoryUrl("https://adb-5245952564735461.0.azuredatabricks.net/api/2.0/mlflow")
+ .build());
+ }
+
+ @Test
+ void testNpm() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:npm/foobar@12.3.1").build(),
+ PackageURLBuilders.npm()
+ .withName("foobar")
+ .withVersion("12.3.1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:npm/%40angular/animation@12.3.1")
+ .build(),
+ PackageURLBuilders.npm()
+ .withScope("@angular")
+ .withName("animation")
+ .withVersion("12.3.1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:npm/mypackage@12.4.5?vcs_url=git://host.com/path/to/repo.git%404345abcd34343")
+ .build(),
+ PackageURLBuilders.npm()
+ .withName("mypackage")
+ .withVersion("12.4.5")
+ .withVcsUrl("git://host.com/path/to/repo.git@4345abcd34343")
+ .build());
+ }
+
+ @Test
+ void testNuget() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:nuget/EnterpriseLibrary.Common@6.0.1304")
+ .build(),
+ PackageURLBuilders.nuget()
+ .withName("EnterpriseLibrary.Common")
+ .withVersion("6.0.1304")
+ .build());
+ }
+
+ @Test
+ void testOci() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:oci/debian@sha256%3A244fd47e07d10?repository_url=docker.io/library/debian&arch=amd64&tag=latest")
+ .build(),
+ PackageURLBuilders.oci()
+ .withName("debian")
+ .withVersion("sha256:244fd47e07d10")
+ .withRepositoryUrl("docker.io/library/debian")
+ .withArch("amd64")
+ .withTag("latest")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:oci/debian@sha256%3A244fd47e07d10?repository_url=ghcr.io/debian&tag=bullseye")
+ .build(),
+ PackageURLBuilders.oci()
+ .withName("debian")
+ .withVersion("sha256:244fd47e07d10")
+ .withRepositoryUrl("ghcr.io/debian")
+ .withTag("bullseye")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:oci/static@sha256%3A244fd47e07d10?repository_url=gcr.io/distroless/static&tag=latest")
+ .build(),
+ PackageURLBuilders.oci()
+ .withName("static")
+ .withVersion("sha256:244fd47e07d10")
+ .withRepositoryUrl("gcr.io/distroless/static")
+ .withTag("latest")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:oci/hello-wasm@sha256:244fd47e07d10?tag=v1")
+ .build(),
+ PackageURLBuilders.oci()
+ .withName("hello-wasm")
+ .withVersion("sha256:244fd47e07d10")
+ .withTag("v1")
+ .build());
+ }
+
+ @Test
+ void testPub() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:pub/characters@1.2.0").build(),
+ PackageURLBuilders.pub()
+ .withName("characters")
+ .withVersion("1.2.0")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:pub/flutter@0.0.0").build(),
+ PackageURLBuilders.pub()
+ .withName("flutter")
+ .withVersion("0.0.0")
+ .build());
+ }
+
+ @Test
+ void testPypi() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:pypi/django@1.11.1").build(),
+ PackageURLBuilders.pypi()
+ .withName("django")
+ .withVersion("1.11.1")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:pypi/django@1.11.1?file_name=Django-1.11.1.tar.gz")
+ .build(),
+ PackageURLBuilders.pypi()
+ .withName("django")
+ .withVersion("1.11.1")
+ .withFileName("Django-1.11.1.tar.gz")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:pypi/django@1.11.1?file_name=Django-1.11.1-py2.py3-none-any.whl")
+ .build(),
+ PackageURLBuilders.pypi()
+ .withName("django")
+ .withVersion("1.11.1")
+ .withFileName("Django-1.11.1-py2.py3-none-any.whl")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:pypi/django-allauth@12.23").build(),
+ PackageURLBuilders.pypi()
+ .withName("django-allauth")
+ .withVersion("12.23")
+ .build());
+ }
+
+ @Test
+ void testQpkg() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:qpkg/blackberry/com.qnx.sdp@7.0.0.SGA201702151847")
+ .build(),
+ PackageURLBuilders.qpkg()
+ .withVendor("blackberry")
+ .withName("com.qnx.sdp")
+ .withVersion("7.0.0.SGA201702151847")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:qpkg/blackberry/com.qnx.qnx710.foo.bar.qux@0.0.4.01449T202205040833L")
+ .build(),
+ PackageURLBuilders.qpkg()
+ .withVendor("blackberry")
+ .withName("com.qnx.qnx710.foo.bar.qux")
+ .withVersion("0.0.4.01449T202205040833L")
+ .build());
+ }
+
+ @Test
+ void testRpm() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:rpm/fedora/curl@7.50.3-1.fc25?arch=i386&distro=fedora-25")
+ .build(),
+ PackageURLBuilders.rpm()
+ .withVendor("fedora")
+ .withName("curl")
+ .withVersionRelease("7.50.3-1.fc25")
+ .withArch("i386")
+ .withDistro("fedora-25")
+ .build());
+ // FIXME: Example missing required vendor
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:rpm/fedora/centerim@4.22.10-1.el6?arch=i686&epoch=1&distro=fedora-25")
+ .build(),
+ PackageURLBuilders.rpm()
+ .withVendor("fedora")
+ .withName("centerim")
+ .withVersionRelease("4.22.10-1.el6")
+ .withArch("i686")
+ .withEpoch("1")
+ .withDistro("fedora-25")
+ .build());
+ }
+
+ @Test
+ void testSwid() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:swid/Acme/example.com%2FEnterprise+Server@1.0.0?tag_id=75b8c285-fa7b-485b-b199-4745e3004d0d")
+ .build(),
+ PackageURLBuilders.swid()
+ .withSoftwareCreator("Acme")
+ .withSoftwareIdentityName("example.com/Enterprise+Server")
+ .withSoftwareIdentityVersion("1.0.0")
+ .withTagId("75b8c285-fa7b-485b-b199-4745e3004d0d")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:swid/Fedora@29?tag_id=org.fedoraproject.Fedora-29")
+ .build(),
+ PackageURLBuilders.swid()
+ .withSoftwareIdentityName("Fedora")
+ .withSoftwareIdentityVersion("29")
+ .withTagId("org.fedoraproject.Fedora-29")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL(
+ "pkg:swid/Adobe%2BSystems%2BIncorporated/Adobe%2BInDesign@CC?tag_id=CreativeCloud-CS6-Win-GM-MUL")
+ .build(),
+ PackageURLBuilders.swid()
+ .withSoftwareCreator("Adobe+Systems+Incorporated")
+ .withSoftwareIdentityName("Adobe+InDesign")
+ .withSoftwareIdentityVersion("CC")
+ .withTagId("CreativeCloud-CS6-Win-GM-MUL")
+ .build());
+ }
+
+ @Test
+ void testSwift() throws MalformedPackageURLException {
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:swift/github.com/Alamofire/Alamofire@5.4.3")
+ .build(),
+ PackageURLBuilders.swift()
+ .withNamespace("github.com/Alamofire")
+ .withRepositoryName("Alamofire")
+ .withPackageVersion("5.4.3")
+ .build());
+ assertEquals(
+ PackageURLBuilder.aPackageURL("pkg:swift/github.com/RxSwiftCommunity/RxFlow@2.12.4")
+ .build(),
+ PackageURLBuilders.swift()
+ .withNamespace("github.com/RxSwiftCommunity")
+ .withRepositoryName("RxFlow")
+ .withPackageVersion("2.12.4")
+ .build());
+ }
+}