3434import java .util .TreeMap ;
3535import java .util .function .IntPredicate ;
3636import java .util .stream .Collectors ;
37+
38+ import com .github .packageurl .type .PackageTypeFactory ;
3739import org .jspecify .annotations .Nullable ;
3840
3941/**
@@ -96,7 +98,7 @@ public PackageURL(final String type, final String name) throws MalformedPackageU
9698 * @deprecated use {@link #PackageURL(String, String, String, String, Map, String)} instead
9799 */
98100 @ Deprecated
99- public PackageURL (final String type , final @ Nullable String namespace , final String name , final @ Nullable String version ,
101+ public PackageURL (final @ Nullable String type , final @ Nullable String namespace , final @ Nullable String name , final @ Nullable String version ,
100102 final @ Nullable TreeMap <String , String > qualifiers , final @ Nullable String subpath )
101103 throws MalformedPackageURLException {
102104 this .type = toLowerCase (validateType (requireNonNull (type , "type" )));
@@ -105,7 +107,8 @@ public PackageURL(final String type, final @Nullable String namespace, final Str
105107 this .version = validateVersion (type , version );
106108 this .qualifiers = parseQualifiers (qualifiers );
107109 this .subpath = validateSubpath (subpath );
108- verifyTypeConstraints (this .type , this .namespace , this .name );
110+ //verifyTypeConstraints(this.type, this.namespace, this.name);
111+ PackageTypeFactory .getDefault ().validateComponents (type , namespace , name , version , qualifiers , subpath );
109112 }
110113
111114 /**
@@ -121,7 +124,7 @@ public PackageURL(final String type, final @Nullable String namespace, final Str
121124 * @throws NullPointerException if {@code type} or {@code name} are {@code null}
122125 * @since 1.6.0
123126 */
124- public PackageURL (final String type , final @ Nullable String namespace , final String name , final @ Nullable String version ,
127+ public PackageURL (final @ Nullable String type , final @ Nullable String namespace , final @ Nullable String name , final @ Nullable String version ,
125128 final @ Nullable Map <String , @ Nullable String > qualifiers , final @ Nullable String subpath )
126129 throws MalformedPackageURLException {
127130 this (type , namespace , name , version , (qualifiers != null ) ? new TreeMap <>(qualifiers ) : null , subpath );
@@ -280,7 +283,7 @@ private static boolean isValidCharForType(int c) {
280283 return (isAlphaNumeric (c ) || c == '.' || c == '+' || c == '-' );
281284 }
282285
283- private static boolean isValidCharForKey (int c ) {
286+ public static boolean isValidCharForKey (int c ) {
284287 return (isAlphaNumeric (c ) || c == '.' || c == '_' || c == '-' );
285288 }
286289
@@ -438,6 +441,10 @@ private static void validateValue(final String key, final @Nullable String value
438441 }
439442 }
440443
444+ public PackageURL normalize () throws MalformedPackageURLException {
445+ return PackageTypeFactory .getDefault ().normalizeComponents (type , namespace , name , version , qualifiers , subpath );
446+ }
447+
441448 /**
442449 * Returns the canonicalized representation of the purl.
443450 *
@@ -466,6 +473,17 @@ public String canonicalize() {
466473 * @since 1.3.2
467474 */
468475 private String canonicalize (boolean coordinatesOnly ) {
476+ try {
477+ PackageURL packageURL = normalize ();
478+ namespace = packageURL .getNamespace ();
479+ name = packageURL .getName ();
480+ version = packageURL .getVersion ();
481+ qualifiers = packageURL .getQualifiers ();
482+ subpath = packageURL .getSubpath ();
483+ } catch (MalformedPackageURLException e ) {
484+ throw new ValidationException ("Normalization failed" , e );
485+ }
486+
469487 final StringBuilder purl = new StringBuilder ();
470488 purl .append (SCHEME_PART ).append (type ).append ("/" );
471489 if (namespace != null ) {
@@ -523,18 +541,22 @@ private static boolean isUnreserved(int c) {
523541 return (isValidCharForKey (c ) || c == '~' );
524542 }
525543
526- private static boolean isAlpha (int c ) {
544+ public static boolean isAlpha (int c ) {
527545 return (isLowerCase (c ) || isUpperCase (c ));
528546 }
529547
530548 private static boolean isDigit (int c ) {
531549 return (c >= '0' && c <= '9' );
532550 }
533551
534- private static boolean isAlphaNumeric (int c ) {
552+ public static boolean isAlphaNumeric (int c ) {
535553 return (isDigit (c ) || isAlpha (c ));
536554 }
537555
556+ public static boolean isWhitespace (int c ) {
557+ return (c == ' ' || c == '\t' || c == '\r' || c == '\n' );
558+ }
559+
538560 private static boolean isUpperCase (int c ) {
539561 return (c >= 'A' && c <= 'Z' );
540562 }
@@ -559,7 +581,7 @@ private static int toLowerCase(int c) {
559581 return (c ^ 0x20 );
560582 }
561583
562- private static String toLowerCase (String s ) {
584+ public static String toLowerCase (String s ) {
563585 int pos = indexOfFirstUpperCaseChar (s );
564586
565587 if (pos == -1 ) {
@@ -697,7 +719,7 @@ private void parse(final String purl) throws MalformedPackageURLException {
697719 remainder = remainder .substring (0 , index );
698720 this .namespace = validateNamespace (parsePath (remainder .substring (start ), false ));
699721 }
700- verifyTypeConstraints (this .type , this .namespace , this .name );
722+ // verifyTypeConstraints(this.type, this.namespace, this.name);
701723 } catch (URISyntaxException e ) {
702724 throw new MalformedPackageURLException ("Invalid purl: " + e .getMessage (), e );
703725 }
0 commit comments