diff --git a/pom.xml b/pom.xml
index bd8adeaf..7129d507 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,301 +1,323 @@
+
- 4.0.0
- com.github.package-url
- packageurl-java
- 1.6.0-SNAPSHOT
- jar
+ 4.0.0
+ com.github.package-url
+ packageurl-java
+ 1.6.0-SNAPSHOT
+ jar
- Package URL
- The official Java implementation of the PackageURL specification. PackageURL (purl) is a minimal
- specification for describing a package via a "mostly universal" URL.
-
- https://github.com/package-url/packageurl-java
- 2017
+ Package URL
+ The official Java implementation of the PackageURL specification. PackageURL (purl) is a minimal
+ specification for describing a package via a "mostly universal" URL.
+ https://github.com/package-url/packageurl-java
+ 2017
-
-
- MIT
- https://opensource.org/licenses/MIT
- repo
-
-
+
+
+ MIT
+ https://opensource.org/licenses/MIT
+ repo
+
+
-
-
- Steve Springett
- Steve.Springett@owasp.org
- OWASP
- http://www.owasp.org/
-
- Architect
- Developer
-
-
-
+
+
+ Steve Springett
+ Steve.Springett@owasp.org
+ OWASP
+ http://www.owasp.org/
+
+ Architect
+ Developer
+
+
+
-
-
- 8
- UTF-8
- UTF-8
- false
+
+ scm:git:git@github.com:package-url/packageurl-java.git
+ scm:git:git@github.com:package-url/packageurl-java.git
+ HEAD
+ https://github.com/package-url/packageurl-java.git
+
-
+ 8
+ UTF-8
+ UTF-8
+ false
+
+
- 2025-03-15T10:12:28Z
+ 2025-03-15T10:12:28Z
-
-
+
- 17
-
- 18
-
-
- 7.1.0
- 3.6.0
- 2.9.1
- 3.4.1
- 3.14.0
- 3.1.4
- 3.5.0
- 3.2.7
- 3.1.4
- 3.4.2
- 3.11.2
- 3.1.1
- 3.3.1
- 3.21.0
- 3.3.1
- 3.5.2
- 2.18.0
-
- 2.36.0
- 0.8.12
- 4.9.3.0
- 4.9.3
-
- 3.1.1
- 20250107
- 5.12.1
- 1.4.0
-
+ 18
-
- scm:git:git@github.com:package-url/packageurl-java.git
- https://github.com/package-url/packageurl-java.git
- scm:git:git@github.com:package-url/packageurl-java.git
- HEAD
-
-
-
- GitHub
- https://github.com/package-url/packageurl-java/issues
-
-
-
- travis-ci
- https://travis-ci.com/package-url/packageurl-java
-
-
-
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
-
-
-
-
-
-
-
- org.junit
- junit-bom
- ${junit-bom.version}
- pom
- import
-
-
-
+
+ 7.1.0
+ 3.6.0
+ 2.9.1
+ 3.4.1
+ 3.14.0
+ 3.1.4
+ 3.5.0
+ 3.2.7
+ 3.1.4
+ 3.4.2
+ 3.11.2
+ 3.1.1
+ 3.3.1
+ 3.21.0
+ 3.3.1
+ 3.5.2
+ 2.18.0
+
+ 2.36.0
+ 0.8.12
+ 2.58.0
+ 4.9.3.0
+ 2.44.3
+ 4.9.3
+
+ 3.1.1
+ 20250107
+ 5.12.1
+ 1.4.0
+
+
-
- jakarta.validation
- jakarta.validation-api
- ${jakarta.validation-api.version}
- true
- provided
-
-
- org.osgi
- org.osgi.annotation.bundle
- 2.0.0
- provided
-
-
- org.jspecify
- jspecify
- 1.0.0
- provided
- true
-
-
- org.json
- json
- ${json.version}
- test
-
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
- org.junit.jupiter
- junit-jupiter-params
- test
-
+
+
+ org.junit
+ junit-bom
+ ${junit-bom.version}
+ pom
+ import
+
+
-
-
-
-
- biz.aQute.bnd
- bnd-baseline-maven-plugin
- ${bnd.maven.plugin.version}
-
-
- biz.aQute.bnd
- bnd-maven-plugin
- ${bnd.maven.plugin.version}
-
-
- org.apache.maven.plugins
- maven-clean-plugin
- ${maven.clean.plugin.version}
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
- ${maven.compiler.plugin.version}
-
-
- org.apache.maven.plugins
- maven-deploy-plugin
- ${maven.deploy.plugin.version}
-
-
- org.apache.maven.plugins
- maven-enforcer-plugin
- ${maven.enforcer.plugin.version}
-
-
- org.apache.maven.plugins
- maven-install-plugin
- ${maven.install.plugin.version}
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
- ${maven-gpg-plugin.version}
-
-
- org.apache.maven.plugins
- maven-jar-plugin
- ${maven.jar.plugin.version}
-
-
- org.apache.maven.plugins
- maven-release-plugin
- ${maven.release.plugin.version}
-
-
- org.apache.maven.plugins
- maven-resources-plugin
- ${maven.resources.plugin.version}
-
-
- org.apache.maven.plugins
- maven-site-plugin
- ${maven.site.plugin.version}
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
- ${maven.surefire.plugin.version}
-
-
-
-
-
-
- org.codehaus.mojo
- build-helper-maven-plugin
- ${builder.helper.maven.plugin.version}
-
-
- parse-version
-
- parse-version
-
- validate
-
-
-
-
- org.apache.maven.plugins
- maven-enforcer-plugin
-
-
- enforce-build-environment
-
- enforce
-
-
-
-
- true
- [${min.jdk.version},)
- To build this library you need JDK ${min.jdk.version} or higher.
-
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
- ${maven.compiler.release}
- true
-
- -Xlint:all
-
- -XDcompilePolicy=simple
- --should-stop=ifError=FLOW
- -Xplugin:ErrorProne
-
+ -XDcompilePolicy=simple
+ --should-stop=ifError=FLOW
+ -Xplugin:ErrorProne
+
-
-
-
- com.google.errorprone
- error_prone_core
- ${error.prone.core.version}
-
-
-
-
-
- com.github.spotbugs
- spotbugs-maven-plugin
- ${spotbugs.maven.plugin.version}
-
-
- package
-
- check
-
-
-
-
-
-
- com.github.spotbugs
- spotbugs
- ${com.github.spotbugs.version}
-
-
-
-
- org.codehaus.mojo
- versions-maven-plugin
- ${versions-maven-plugin.version}
-
- (?i).+[-.](alpha|beta|cr|dev|m|rc)([-.]?\d+)?
-
-
-
- org.jacoco
- jacoco-maven-plugin
- ${jacoco.maven.plugin.version}
-
-
- setup
-
- prepare-agent
-
-
-
- report
- prepare-package
-
- report
-
-
-
-
-
- org.apache.maven.plugins
- maven-source-plugin
- ${maven.source.plugin.version}
-
-
- attach-sources
-
- jar-no-fork
-
-
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- ${maven.javadoc.plugin.version}
-
-
- attach-javadocs
-
- jar
-
-
-
-
-
+ true
+
+
+
+
+
+ check-formatting
+
+ check
+
+
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+ ${spotbugs.maven.plugin.version}
+
+
+
+ com.github.spotbugs
+ spotbugs
+ ${com.github.spotbugs.version}
+
+
+
+
+
+ check
+
+ package
+
+
+
+
+ org.codehaus.mojo
+ versions-maven-plugin
+ ${versions-maven-plugin.version}
+
+ (?i).+[-.](alpha|beta|cr|dev|m|rc)([-.]?\d+)?
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${jacoco.maven.plugin.version}
+
+
+ setup
+
+ prepare-agent
+
+
+
+ report
+
+ report
+
+ prepare-package
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${maven.source.plugin.version}
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven.javadoc.plugin.version}
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
-
- biz.aQute.bnd
- bnd-maven-plugin
-
- true
-
-
- generate-jar-and-module-descriptors
-
- jar
-
-
-
-
-
+ true
+
+
+ generate-jar-and-module-descriptors
+
+ jar
+
+
+
+
+
-
- biz.aQute.bnd
- bnd-baseline-maven-plugin
-
-
- check-api-compatibility
-
- baseline
-
-
-
-
-
- org.cyclonedx
- cyclonedx-maven-plugin
- ${cyclonedx-maven-plugin.version}
-
- library
-
-
-
- package
-
- makeBom
-
-
-
-
-
-
+
+ biz.aQute.bnd
+ bnd-baseline-maven-plugin
+
+
+ check-api-compatibility
+
+ baseline
+
+
+
+
+
+ org.cyclonedx
+ cyclonedx-maven-plugin
+ ${cyclonedx-maven-plugin.version}
+
+ library
+
+
+
+
+ makeBom
+
+ package
+
+
+
+
+
-
-
-
- java8-tests
-
-
- ${user.home}/.m2/toolchains.xml
-
-
-
-
-
- org.apache.maven.plugins
- maven-surefire-plugin
-
-
- default-test
-
-
- [1.8,9)
-
-
-
-
-
-
- me.fabriciorby
- maven-surefire-junit5-tree-reporter
- ${maven-surefire-junit5-tree-reporter.version}
-
-
-
- plain
-
- true
-
-
-
-
-
-
-
-
-
- release
-
- false
-
-
-
-
- org.apache.maven.plugins
- maven-enforcer-plugin
-
-
- enforce-build-environment
-
-
-
- true
- [${min.jdk.version},${max.jdk.version})
- To release this library you need JDK ${min.jdk.version}.
-
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-gpg-plugin
-
-
- sign-artifacts
- verify
-
- sign
-
-
-
-
-
-
-
-
+
+ java8-tests
+
+
+ ${user.home}/.m2/toolchains.xml
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+ plain
+
+ true
+
+
+
+
+
+ me.fabriciorby
+ maven-surefire-junit5-tree-reporter
+ ${maven-surefire-junit5-tree-reporter.version}
+
+
+
+
+ default-test
+
+
+ [1.8,9)
+
+
+
+
+
+
+
+
+
+ release
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+
+
+ enforce-build-environment
+
+
+
+ true
+ [${min.jdk.version},${max.jdk.version})
+ To release this library you need JDK ${min.jdk.version}.
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+
+
+ sign-artifacts
+
+ sign
+
+ verify
+
+
+
+
+
+
+
diff --git a/src/headers/java.txt b/src/headers/java.txt
new file mode 100644
index 00000000..9f5faf02
--- /dev/null
+++ b/src/headers/java.txt
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
\ No newline at end of file
diff --git a/src/headers/xml.txt b/src/headers/xml.txt
new file mode 100644
index 00000000..080595b8
--- /dev/null
+++ b/src/headers/xml.txt
@@ -0,0 +1,22 @@
+
+
\ No newline at end of file
diff --git a/src/main/java/com/github/packageurl/MalformedPackageURLException.java b/src/main/java/com/github/packageurl/MalformedPackageURLException.java
index b58c9532..775a1313 100644
--- a/src/main/java/com/github/packageurl/MalformedPackageURLException.java
+++ b/src/main/java/com/github/packageurl/MalformedPackageURLException.java
@@ -37,8 +37,7 @@ public class MalformedPackageURLException extends Exception {
*
* @since 1.0.0
*/
- public MalformedPackageURLException() {
- }
+ public MalformedPackageURLException() {}
/**
* Constructs a {@code MalformedPackageURLException} with the
diff --git a/src/main/java/com/github/packageurl/PackageURL.java b/src/main/java/com/github/packageurl/PackageURL.java
index a90577f2..42a84a56 100644
--- a/src/main/java/com/github/packageurl/PackageURL.java
+++ b/src/main/java/com/github/packageurl/PackageURL.java
@@ -100,8 +100,13 @@ public PackageURL(final String type, final String name) throws MalformedPackageU
* @deprecated use {@link #PackageURL(String, String, String, String, Map, String)} instead
*/
@Deprecated
- public PackageURL(final String type, final @Nullable String namespace, final String name, final @Nullable String version,
- final @Nullable TreeMap qualifiers, final @Nullable String subpath)
+ public PackageURL(
+ final String type,
+ final @Nullable String namespace,
+ final String name,
+ final @Nullable String version,
+ final @Nullable TreeMap qualifiers,
+ final @Nullable String subpath)
throws MalformedPackageURLException {
this.type = toLowerCase(validateType(requireNonNull(type, "type")));
this.namespace = validateNamespace(namespace);
@@ -125,8 +130,13 @@ public PackageURL(final String type, final @Nullable String namespace, final Str
* @throws NullPointerException if {@code type} or {@code name} are {@code null}
* @since 1.6.0
*/
- public PackageURL(final String type, final @Nullable String namespace, final String name, final @Nullable String version,
- final @Nullable Map qualifiers, final @Nullable String subpath)
+ public PackageURL(
+ final String type,
+ final @Nullable String namespace,
+ final String name,
+ final @Nullable String version,
+ final @Nullable Map qualifiers,
+ final @Nullable String subpath)
throws MalformedPackageURLException {
this(type, namespace, name, version, (qualifiers != null) ? new TreeMap<>(qualifiers) : null, subpath);
}
@@ -264,10 +274,11 @@ public Map getQualifiers() {
return subpath;
}
- private void validateScheme(final String value) throws MalformedPackageURLException {
- if (!SCHEME.equals(value)) {
- throw new MalformedPackageURLException("The PackageURL scheme '" + value + "' is invalid. It should be '" + SCHEME + "'");
- }
+ private void validateScheme(final String value) throws MalformedPackageURLException {
+ if (!SCHEME.equals(value)) {
+ throw new MalformedPackageURLException(
+ "The PackageURL scheme '" + value + "' is invalid. It should be '" + SCHEME + "'");
+ }
}
private static String validateType(final String value) throws MalformedPackageURLException {
@@ -288,17 +299,23 @@ private static boolean isValidCharForKey(int c) {
return (isAlphaNumeric(c) || c == '.' || c == '_' || c == '-');
}
- private static void validateChars(String value, IntPredicate predicate, String component) throws MalformedPackageURLException {
+ private static void validateChars(String value, IntPredicate predicate, String component)
+ throws MalformedPackageURLException {
char firstChar = value.charAt(0);
if (isDigit(firstChar)) {
- throw new MalformedPackageURLException("The PackageURL " + component + " cannot start with a number: " + firstChar);
+ throw new MalformedPackageURLException(
+ "The PackageURL " + component + " cannot start with a number: " + firstChar);
}
- String invalidChars = value.chars().filter(predicate.negate()).mapToObj(c -> String.valueOf((char) c)).collect(Collectors.joining(", "));
+ String invalidChars = value.chars()
+ .filter(predicate.negate())
+ .mapToObj(c -> String.valueOf((char) c))
+ .collect(Collectors.joining(", "));
if (!invalidChars.isEmpty()) {
- throw new MalformedPackageURLException("The PackageURL " + component + " '" + value + "' contains invalid characters: " + invalidChars);
+ throw new MalformedPackageURLException(
+ "The PackageURL " + component + " '" + value + "' contains invalid characters: " + invalidChars);
}
}
@@ -331,7 +348,8 @@ private static void validateChars(String value, IntPredicate predicate, String c
case StandardTypes.MLFLOW:
case StandardTypes.OCI:
if (tempNamespace != null) {
- throw new MalformedPackageURLException("The PackageURL specified contains a namespace which is not allowed for type: " + type);
+ throw new MalformedPackageURLException(
+ "The PackageURL specified contains a namespace which is not allowed for type: " + type);
}
retVal = null;
break;
@@ -388,7 +406,8 @@ private String validateName(final String value) throws MalformedPackageURLExcept
}
}
- private @Nullable Map validateQualifiers(final @Nullable Map values) throws MalformedPackageURLException {
+ private @Nullable Map validateQualifiers(final @Nullable Map values)
+ throws MalformedPackageURLException {
if (values == null || values.isEmpty()) {
return null;
}
@@ -409,9 +428,11 @@ private static void validateKey(final @Nullable String value) throws MalformedPa
validateChars(value, PackageURL::isValidCharForKey, "qualifier key");
}
- private static void validateValue(final String key, final @Nullable String value) throws MalformedPackageURLException {
+ private static void validateValue(final String key, final @Nullable String value)
+ throws MalformedPackageURLException {
if (isEmpty(value)) {
- throw new MalformedPackageURLException("The specified PackageURL contains an empty or null qualifier value for key " + key);
+ throw new MalformedPackageURLException(
+ "The specified PackageURL contains an empty or null qualifier value for key " + key);
}
}
@@ -422,7 +443,8 @@ private static void validateValue(final String key, final @Nullable String value
return validatePath(value.split("/"), true);
}
- private static @Nullable String validatePath(final String[] segments, final boolean isSubPath) throws MalformedPackageURLException {
+ private static @Nullable String validatePath(final String[] segments, final boolean isSubPath)
+ throws MalformedPackageURLException {
if (segments.length == 0) {
return null;
}
@@ -430,13 +452,16 @@ private static void validateValue(final String key, final @Nullable String value
return Arrays.stream(segments)
.peek(segment -> {
if (isSubPath && ("..".equals(segment) || ".".equals(segment))) {
- throw new ValidationException("Segments in the subpath may not be a period ('.') or repeated period ('..')");
+ throw new ValidationException(
+ "Segments in the subpath may not be a period ('.') or repeated period ('..')");
} else if (segment.contains("/")) {
- throw new ValidationException("Segments in the namespace and subpath may not contain a forward slash ('/')");
+ throw new ValidationException(
+ "Segments in the namespace and subpath may not contain a forward slash ('/')");
} else if (segment.isEmpty()) {
throw new ValidationException("Segments in the namespace and subpath may not be empty");
}
- }).collect(Collectors.joining("/"));
+ })
+ .collect(Collectors.joining("/"));
} catch (ValidationException e) {
throw new MalformedPackageURLException(e);
}
@@ -567,16 +592,23 @@ private static String toLowerCase(String s) {
}
private static int indexOfPercentChar(final byte[] bytes, final int start) {
- return IntStream.range(start, bytes.length).filter(i -> isPercent(bytes[i])).findFirst().orElse(-1);
+ return IntStream.range(start, bytes.length)
+ .filter(i -> isPercent(bytes[i]))
+ .findFirst()
+ .orElse(-1);
}
private static int indexOfUnsafeChar(final byte[] bytes, final int start) {
- return IntStream.range(start, bytes.length).filter(i -> shouldEncode(bytes[i])).findFirst().orElse(-1);
+ return IntStream.range(start, bytes.length)
+ .filter(i -> shouldEncode(bytes[i]))
+ .findFirst()
+ .orElse(-1);
}
private static byte percentDecode(final byte[] bytes, final int start) {
if (start + 2 >= bytes.length) {
- throw new ValidationException("Incomplete percent encoding at offset " + start + " with value '" + new String(bytes, start, bytes.length - start, StandardCharsets.UTF_8) + "'");
+ throw new ValidationException("Incomplete percent encoding at offset " + start + " with value '"
+ + new String(bytes, start, bytes.length - start, StandardCharsets.UTF_8) + "'");
}
int pos1 = start + 1;
@@ -584,7 +616,8 @@ private static byte percentDecode(final byte[] bytes, final int start) {
int c1 = Character.digit(b1, 16);
if (c1 == -1) {
- throw new ValidationException("Invalid percent encoding char 1 at offset " + pos1 + " with value '" + ((char) b1) + "'");
+ throw new ValidationException(
+ "Invalid percent encoding char 1 at offset " + pos1 + " with value '" + ((char) b1) + "'");
}
int pos2 = pos1 + 1;
@@ -592,7 +625,8 @@ private static byte percentDecode(final byte[] bytes, final int start) {
int c2 = Character.digit(bytes[pos2], 16);
if (c2 == -1) {
- throw new ValidationException("Invalid percent encoding char 2 at offset " + pos2 + " with value '" + ((char) b2) + "'");
+ throw new ValidationException(
+ "Invalid percent encoding char 2 at offset " + pos2 + " with value '" + ((char) b2) + "'");
}
return ((byte) ((c1 << 4) + c2));
@@ -711,7 +745,8 @@ private void parse(final String purl) throws MalformedPackageURLException {
try {
if (!purl.startsWith(SCHEME_PART)) {
- throw new MalformedPackageURLException("Invalid purl: " + purl + ". It does not start with '" + SCHEME_PART + "'");
+ throw new MalformedPackageURLException(
+ "Invalid purl: " + purl + ". It does not start with '" + SCHEME_PART + "'");
}
final int length = purl.length();
@@ -739,7 +774,6 @@ private void parse(final String purl) throws MalformedPackageURLException {
final String rawQuery = uri.getRawQuery();
if (rawQuery != null && !rawQuery.isEmpty()) {
this.qualifiers = parseQualifiers(rawQuery);
-
}
// this is the rest of the purl that needs to be parsed
String remainder = uri.getRawPath();
@@ -788,15 +822,18 @@ private void parse(final String purl) throws MalformedPackageURLException {
* @param namespace the purl namespace
* @throws MalformedPackageURLException if constraints are not met
*/
- private void verifyTypeConstraints(String type, @Nullable String namespace, @Nullable String name) throws MalformedPackageURLException {
+ private void verifyTypeConstraints(String type, @Nullable String namespace, @Nullable String name)
+ throws MalformedPackageURLException {
if (StandardTypes.MAVEN.equals(type)) {
if (isEmpty(namespace) || isEmpty(name)) {
- throw new MalformedPackageURLException("The PackageURL specified is invalid. Maven requires both a namespace and name.");
+ throw new MalformedPackageURLException(
+ "The PackageURL specified is invalid. Maven requires both a namespace and name.");
}
}
}
- private @Nullable Map parseQualifiers(final @Nullable Map qualifiers) throws MalformedPackageURLException {
+ private @Nullable Map parseQualifiers(final @Nullable Map qualifiers)
+ throws MalformedPackageURLException {
if (qualifiers == null || qualifiers.isEmpty()) {
return null;
}
@@ -804,7 +841,8 @@ private void verifyTypeConstraints(String type, @Nullable String namespace, @Nul
try {
final TreeMap results = qualifiers.entrySet().stream()
.filter(entry -> !isEmpty(entry.getValue()))
- .collect(TreeMap::new,
+ .collect(
+ TreeMap::new,
(map, value) -> map.put(toLowerCase(value.getKey()), value.getValue()),
TreeMap::putAll);
return validateQualifiers(results);
@@ -813,17 +851,21 @@ private void verifyTypeConstraints(String type, @Nullable String namespace, @Nul
}
}
- @SuppressWarnings("StringSplitter")//reason: surprising behavior is okay in this case
- private @Nullable Map parseQualifiers(final String encodedString) throws MalformedPackageURLException {
+ @SuppressWarnings("StringSplitter") // reason: surprising behavior is okay in this case
+ private @Nullable Map parseQualifiers(final String encodedString)
+ throws MalformedPackageURLException {
try {
final TreeMap results = Arrays.stream(encodedString.split("&"))
- .collect(TreeMap::new,
+ .collect(
+ TreeMap::new,
(map, value) -> {
final String[] entry = value.split("=", 2);
if (entry.length == 2 && !entry[1].isEmpty()) {
String key = toLowerCase(entry[0]);
if (map.put(key, percentDecode(entry[1])) != null) {
- throw new ValidationException("Duplicate package qualifier encountered. More then one value was specified for " + key);
+ throw new ValidationException(
+ "Duplicate package qualifier encountered. More then one value was specified for "
+ + key);
}
}
},
@@ -857,7 +899,7 @@ private String encodePath(final String path) {
* @return true if equivalence passes, false if not
* @since 1.2.0
*/
- //@Deprecated(since = "1.4.0", forRemoval = true)
+ // @Deprecated(since = "1.4.0", forRemoval = true)
@Deprecated
public boolean isBaseEquals(final PackageURL purl) {
return isCoordinatesEquals(purl);
@@ -873,10 +915,10 @@ public boolean isBaseEquals(final PackageURL purl) {
* @since 1.4.0
*/
public boolean isCoordinatesEquals(final PackageURL purl) {
- return type.equals(purl.type) &&
- Objects.equals(namespace, purl.namespace) &&
- name.equals(purl.name) &&
- Objects.equals(version, purl.version);
+ return type.equals(purl.type)
+ && Objects.equals(namespace, purl.namespace)
+ && name.equals(purl.name)
+ && Objects.equals(version, purl.version);
}
/**
@@ -912,12 +954,12 @@ public boolean equals(@Nullable Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final PackageURL other = (PackageURL) o;
- return type.equals(other.type) &&
- Objects.equals(namespace, other.namespace) &&
- name.equals(other.name) &&
- Objects.equals(version, other.version) &&
- Objects.equals(qualifiers, other.qualifiers) &&
- Objects.equals(subpath, other.subpath);
+ return type.equals(other.type)
+ && Objects.equals(namespace, other.namespace)
+ && name.equals(other.name)
+ && Objects.equals(version, other.version)
+ && Objects.equals(qualifiers, other.qualifiers)
+ && Objects.equals(subpath, other.subpath);
}
@Override
@@ -964,13 +1006,13 @@ public static final class StandardTypes {
public static final String RPM = "rpm";
public static final String SWID = "swid";
public static final String SWIFT = "swift";
+
@Deprecated
public static final String DEBIAN = "deb";
+
@Deprecated
public static final String NIXPKGS = "nix";
- private StandardTypes() {
-
- }
+ private StandardTypes() {}
}
}
diff --git a/src/main/java/com/github/packageurl/PackageURLBuilder.java b/src/main/java/com/github/packageurl/PackageURLBuilder.java
index 4544341f..a82c7f8c 100644
--- a/src/main/java/com/github/packageurl/PackageURLBuilder.java
+++ b/src/main/java/com/github/packageurl/PackageURLBuilder.java
@@ -216,7 +216,6 @@ public PackageURLBuilder withoutQualifiers(final Set keys) {
return this;
}
-
/**
* Removes all qualifiers, if any.
* @return a reference to this builder.
diff --git a/src/main/java/com/github/packageurl/validator/PackageURL.java b/src/main/java/com/github/packageurl/validator/PackageURL.java
index 15c916cd..cfbb6950 100644
--- a/src/main/java/com/github/packageurl/validator/PackageURL.java
+++ b/src/main/java/com/github/packageurl/validator/PackageURL.java
@@ -33,14 +33,15 @@
* the JSR-303 compliant validator to validate the field to ensure it meets the Package URL specification.
* @since 1.3.0
*/
-@Target({ ElementType.FIELD})
+@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PackageURLConstraintValidator.class)
public @interface PackageURL {
- String message() default "The Package URL (purl) must be a valid URI and conform to https://github.com/package-url/purl-spec";
+ String message() default
+ "The Package URL (purl) must be a valid URI and conform to https://github.com/package-url/purl-spec";
- Class>[] groups() default { };
+ Class>[] groups() default {};
- Class extends Payload>[] payload() default { };
+ Class extends Payload>[] payload() default {};
}
diff --git a/src/test/java/com/github/packageurl/PackageURLBuilderTest.java b/src/test/java/com/github/packageurl/PackageURLBuilderTest.java
index 03a75060..046184a1 100644
--- a/src/test/java/com/github/packageurl/PackageURLBuilderTest.java
+++ b/src/test/java/com/github/packageurl/PackageURLBuilderTest.java
@@ -45,16 +45,19 @@ static Stream packageURLBuilder() throws IOException {
@ParameterizedTest(name = "{0}: {1}")
@MethodSource
- void packageURLBuilder(String description,
- @Nullable String ignoredPurl,
- PurlParameters parameters,
- String canonicalPurl,
- boolean invalid) throws MalformedPackageURLException {
+ void packageURLBuilder(
+ String description,
+ @Nullable String ignoredPurl,
+ PurlParameters parameters,
+ String canonicalPurl,
+ boolean invalid)
+ throws MalformedPackageURLException {
if (parameters.getType() == null || parameters.getName() == null) {
assertTrue(invalid, "valid test case with type or name `null`");
return;
}
- PackageURLBuilder builder = PackageURLBuilder.aPackageURL().withType(parameters.getType()).withName(parameters.getName());
+ PackageURLBuilder builder =
+ PackageURLBuilder.aPackageURL().withType(parameters.getType()).withName(parameters.getName());
String namespace = parameters.getNamespace();
if (namespace != null) {
builder.withNamespace(namespace);
@@ -87,7 +90,7 @@ void packageURLBuilderException1() throws MalformedPackageURLException {
PackageURL purl = PackageURLBuilder.aPackageURL()
.withType("type")
.withName("name")
- .withQualifier("key","")
+ .withQualifier("key", "")
.build();
assertEquals(0, purl.getQualifiers().size(), "qualifier count");
}
@@ -97,63 +100,78 @@ void packageURLBuilderException1Null() throws MalformedPackageURLException {
PackageURL purl = PackageURLBuilder.aPackageURL()
.withType("type")
.withName("name")
- .withQualifier("key",null)
+ .withQualifier("key", null)
.build();
assertEquals(0, purl.getQualifiers().size(), "qualifier count");
}
@Test
void packageURLBuilderException2() {
- assertThrowsExactly(MalformedPackageURLException.class, () -> {
- PackageURLBuilder.aPackageURL()
- .withType("type")
- .withNamespace("invalid//namespace")
- .withName("name")
- .build();
- }, "Build should fail due to invalid namespace");
+ assertThrowsExactly(
+ MalformedPackageURLException.class,
+ () -> {
+ PackageURLBuilder.aPackageURL()
+ .withType("type")
+ .withNamespace("invalid//namespace")
+ .withName("name")
+ .build();
+ },
+ "Build should fail due to invalid namespace");
}
@Test
void packageURLBuilderException3() {
- assertThrowsExactly(MalformedPackageURLException.class, () -> {
- PackageURLBuilder.aPackageURL()
- .withType("typ^e")
- .withSubpath("invalid/name%2Fspace")
- .withName("name")
- .build();
- }, "Build should fail due to invalid subpath");
+ assertThrowsExactly(
+ MalformedPackageURLException.class,
+ () -> {
+ PackageURLBuilder.aPackageURL()
+ .withType("typ^e")
+ .withSubpath("invalid/name%2Fspace")
+ .withName("name")
+ .build();
+ },
+ "Build should fail due to invalid subpath");
}
@Test
void packageURLBuilderException4() {
- assertThrowsExactly(MalformedPackageURLException.class, () -> {
- PackageURLBuilder.aPackageURL()
- .withType("0_type")
- .withName("name")
- .build();
- }, "Build should fail due to invalid type");
+ assertThrowsExactly(
+ MalformedPackageURLException.class,
+ () -> {
+ PackageURLBuilder.aPackageURL()
+ .withType("0_type")
+ .withName("name")
+ .build();
+ },
+ "Build should fail due to invalid type");
}
@Test
void packageURLBuilderException5() {
- assertThrowsExactly(MalformedPackageURLException.class, () -> {
- PackageURLBuilder.aPackageURL()
- .withType("ype")
- .withName("name")
- .withQualifier("0_key", "value")
- .build();
- }, "Build should fail due to invalid qualifier key");
+ assertThrowsExactly(
+ MalformedPackageURLException.class,
+ () -> {
+ PackageURLBuilder.aPackageURL()
+ .withType("ype")
+ .withName("name")
+ .withQualifier("0_key", "value")
+ .build();
+ },
+ "Build should fail due to invalid qualifier key");
}
@Test
void packageURLBuilderException6() {
- assertThrowsExactly(MalformedPackageURLException.class, () -> {
- PackageURLBuilder.aPackageURL()
- .withType("ype")
- .withName("name")
- .withQualifier("", "value")
- .build();
- }, "Build should fail due to invalid qualifier key");
+ assertThrowsExactly(
+ MalformedPackageURLException.class,
+ () -> {
+ PackageURLBuilder.aPackageURL()
+ .withType("ype")
+ .withName("name")
+ .withQualifier("", "value")
+ .build();
+ },
+ "Build should fail due to invalid qualifier key");
}
@Test
@@ -224,8 +242,6 @@ private void assertBuilderMatch(PackageURL expected, PackageURLBuilder actual) t
assertEquals(eQualifiers, aQualifiers);
- eQualifiers.forEach((k, v) ->
- assertEquals(v, actual.getQualifier(k)));
+ eQualifiers.forEach((k, v) -> assertEquals(v, actual.getQualifier(k)));
}
-
}
diff --git a/src/test/java/com/github/packageurl/PackageURLTest.java b/src/test/java/com/github/packageurl/PackageURLTest.java
index 47eb5960..9dd58ee5 100644
--- a/src/test/java/com/github/packageurl/PackageURLTest.java
+++ b/src/test/java/com/github/packageurl/PackageURLTest.java
@@ -64,16 +64,22 @@ static void resetLocale() {
void validPercentEncoding() throws MalformedPackageURLException {
PackageURL purl = new PackageURL("maven", "com.google.summit", "summit-ast", "2.2.0\n", null, null);
assertEquals("pkg:maven/com.google.summit/summit-ast@2.2.0%0A", purl.toString());
- PackageURL purl2 = new PackageURL("pkg:nuget/%D0%9Cicros%D0%BEft.%D0%95ntit%D1%83Fram%D0%B5work%D0%A1%D0%BEr%D0%B5");
+ PackageURL purl2 =
+ new PackageURL("pkg:nuget/%D0%9Cicros%D0%BEft.%D0%95ntit%D1%83Fram%D0%B5work%D0%A1%D0%BEr%D0%B5");
assertEquals("Мicrosоft.ЕntitуFramеworkСоrе", purl2.getName());
- assertEquals("pkg:nuget/%D0%9Cicros%D0%BEft.%D0%95ntit%D1%83Fram%D0%B5work%D0%A1%D0%BEr%D0%B5", purl2.toString());
+ assertEquals(
+ "pkg:nuget/%D0%9Cicros%D0%BEft.%D0%95ntit%D1%83Fram%D0%B5work%D0%A1%D0%BEr%D0%B5", purl2.toString());
}
@SuppressWarnings("deprecation")
@Test
void invalidPercentEncoding() throws MalformedPackageURLException {
- assertThrowsExactly(MalformedPackageURLException.class, () -> new PackageURL("pkg:maven/com.google.summit/summit-ast@2.2.0%"));
- assertThrowsExactly(MalformedPackageURLException.class, () -> new PackageURL("pkg:maven/com.google.summit/summit-ast@2.2.0%0"));
+ assertThrowsExactly(
+ MalformedPackageURLException.class,
+ () -> new PackageURL("pkg:maven/com.google.summit/summit-ast@2.2.0%"));
+ assertThrowsExactly(
+ MalformedPackageURLException.class,
+ () -> new PackageURL("pkg:maven/com.google.summit/summit-ast@2.2.0%0"));
PackageURL purl = new PackageURL("pkg:maven/com.google.summit/summit-ast@2.2.0");
Throwable t1 = assertThrowsExactly(ValidationException.class, () -> purl.uriDecode("%"));
assertEquals("Incomplete percent encoding at offset 0 with value '%'", t1.getMessage());
@@ -86,19 +92,20 @@ void invalidPercentEncoding() throws MalformedPackageURLException {
}
static Stream constructorParsing() throws IOException {
- return PurlParameters.getTestDataFromFiles("test-suite-data.json",
- "custom-suite.json",
- "string-constructor-only.json");
+ return PurlParameters.getTestDataFromFiles(
+ "test-suite-data.json", "custom-suite.json", "string-constructor-only.json");
}
@DisplayName("Test constructor parsing")
@ParameterizedTest(name = "{0}: ''{1}''")
@MethodSource
- void constructorParsing(String description,
- @Nullable String purlString,
- PurlParameters parameters,
- @Nullable String canonicalPurl,
- boolean invalid) throws Exception {
+ void constructorParsing(
+ String description,
+ @Nullable String purlString,
+ PurlParameters parameters,
+ @Nullable String canonicalPurl,
+ boolean invalid)
+ throws Exception {
if (invalid) {
assertThrows(getExpectedException(purlString), () -> new PackageURL(purlString));
} else {
@@ -109,32 +116,38 @@ void constructorParsing(String description,
}
static Stream constructorParameters() throws IOException {
- return PurlParameters.getTestDataFromFiles("test-suite-data.json", "custom-suite.json", "components-constructor-only.json");
+ return PurlParameters.getTestDataFromFiles(
+ "test-suite-data.json", "custom-suite.json", "components-constructor-only.json");
}
@DisplayName("Test constructor parameters")
@ParameterizedTest(name = "{0}: {2}")
@MethodSource
- void constructorParameters(String description,
- @Nullable String purlString,
- PurlParameters parameters,
- @Nullable String canonicalPurl,
- boolean invalid) throws Exception {
+ void constructorParameters(
+ String description,
+ @Nullable String purlString,
+ PurlParameters parameters,
+ @Nullable String canonicalPurl,
+ boolean invalid)
+ throws Exception {
if (invalid) {
- assertThrows(getExpectedException(parameters),
- () -> new PackageURL(parameters.getType(),
+ assertThrows(
+ getExpectedException(parameters),
+ () -> new PackageURL(
+ parameters.getType(),
+ parameters.getNamespace(),
+ parameters.getName(),
+ parameters.getVersion(),
+ parameters.getQualifiers(),
+ parameters.getSubpath()));
+ } else {
+ PackageURL purl = new PackageURL(
+ parameters.getType(),
parameters.getNamespace(),
parameters.getName(),
parameters.getVersion(),
parameters.getQualifiers(),
- parameters.getSubpath()));
- } else {
- PackageURL purl = new PackageURL(parameters.getType(),
- parameters.getNamespace(),
- parameters.getName(),
- parameters.getVersion(),
- parameters.getQualifiers(),
- parameters.getSubpath());
+ parameters.getSubpath());
assertPurlEquals(parameters, purl);
assertEquals(canonicalPurl, purl.canonicalize(), "canonical PURL");
}
@@ -146,14 +159,16 @@ static Stream constructorTypeNameSpace() throws IOException {
@ParameterizedTest
@MethodSource
- void constructorTypeNameSpace(String description,
- @Nullable String purlString,
- PurlParameters parameters,
- @Nullable String canonicalPurl,
- boolean invalid) throws Exception {
+ void constructorTypeNameSpace(
+ String description,
+ @Nullable String purlString,
+ PurlParameters parameters,
+ @Nullable String canonicalPurl,
+ boolean invalid)
+ throws Exception {
if (invalid) {
- assertThrows(getExpectedException(parameters),
- () -> new PackageURL(parameters.getType(), parameters.getName()));
+ assertThrows(
+ getExpectedException(parameters), () -> new PackageURL(parameters.getType(), parameters.getName()));
} else {
PackageURL purl = new PackageURL(parameters.getType(), parameters.getName());
assertPurlEquals(parameters, purl);
@@ -173,7 +188,9 @@ private static void assertPurlEquals(PurlParameters expected, PackageURL actual)
}
private static Class extends Exception> getExpectedException(PurlParameters json) {
- return json.getType() == null || json.getName() == null ? NullPointerException.class : MalformedPackageURLException.class;
+ return json.getType() == null || json.getName() == null
+ ? NullPointerException.class
+ : MalformedPackageURLException.class;
}
private static Class extends Exception> getExpectedException(@Nullable String purl) {
diff --git a/src/test/java/com/github/packageurl/PurlParameters.java b/src/test/java/com/github/packageurl/PurlParameters.java
index f363020e..e9ea3aa7 100644
--- a/src/test/java/com/github/packageurl/PurlParameters.java
+++ b/src/test/java/com/github/packageurl/PurlParameters.java
@@ -1,3 +1,24 @@
+/*
+ * 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.assertNotNull;
@@ -23,9 +44,11 @@ static Stream getTestDataFromFiles(String... names) throws IOExceptio
try (InputStream is = PackageURLTest.class.getResourceAsStream("/" + name)) {
assertNotNull(is);
JSONArray jsonArray = new JSONArray(new JSONTokener(is));
- result = Stream.concat(result,
- IntStream.range(0,
- jsonArray.length()).mapToObj(jsonArray::getJSONObject).map(PurlParameters::createTestDefinition));
+ result = Stream.concat(
+ result,
+ IntStream.range(0, jsonArray.length())
+ .mapToObj(jsonArray::getJSONObject)
+ .map(PurlParameters::createTestDefinition));
}
}
return result;
@@ -41,16 +64,18 @@ static Stream getTestDataFromFiles(String... names) throws IOExceptio
*
*/
private static Arguments createTestDefinition(JSONObject testDefinition) {
- return Arguments.of(testDefinition.getString("description"),
- testDefinition.optString("purl"),
- new PurlParameters(testDefinition.optString("type", null),
- testDefinition.optString("namespace", null),
- testDefinition.optString("name", null),
- testDefinition.optString("version", null),
- testDefinition.optJSONObject("qualifiers"),
- testDefinition.optString("subpath", null)),
- testDefinition.optString("canonical_purl"),
- testDefinition.getBoolean("is_invalid"));
+ return Arguments.of(
+ testDefinition.getString("description"),
+ testDefinition.optString("purl"),
+ new PurlParameters(
+ testDefinition.optString("type", null),
+ testDefinition.optString("namespace", null),
+ testDefinition.optString("name", null),
+ testDefinition.optString("version", null),
+ testDefinition.optJSONObject("qualifiers"),
+ testDefinition.optString("subpath", null)),
+ testDefinition.optString("canonical_purl"),
+ testDefinition.getBoolean("is_invalid"));
}
private final @Nullable String type;
@@ -60,20 +85,23 @@ private static Arguments createTestDefinition(JSONObject testDefinition) {
private final Map qualifiers;
private final @Nullable String subpath;
- private PurlParameters(@Nullable String type,
- @Nullable String namespace,
- @Nullable String name,
- @Nullable String version,
- @Nullable JSONObject qualifiers,
- @Nullable String subpath) {
+ private PurlParameters(
+ @Nullable String type,
+ @Nullable String namespace,
+ @Nullable String name,
+ @Nullable String version,
+ @Nullable JSONObject qualifiers,
+ @Nullable String subpath) {
this.type = type;
this.namespace = namespace;
this.name = name;
this.version = version;
if (qualifiers != null) {
- this.qualifiers = qualifiers.toMap().entrySet().stream().collect(HashMap::new,
- (m, e) -> m.put(e.getKey(), Objects.toString(e.getValue(), null)),
- HashMap::putAll);
+ this.qualifiers = qualifiers.toMap().entrySet().stream()
+ .collect(
+ HashMap::new,
+ (m, e) -> m.put(e.getKey(), Objects.toString(e.getValue(), null)),
+ HashMap::putAll);
} else {
this.qualifiers = Collections.emptyMap();
}