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[] payload() default { }; + Class[] 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 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 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(); }