Skip to content

GH-1835: Expose qualifiers for generic version scheme - #1837

Merged
cstamas merged 17 commits into
apache:masterfrom
cstamas:issue-1835
Apr 17, 2026
Merged

GH-1835: Expose qualifiers for generic version scheme#1837
cstamas merged 17 commits into
apache:masterfrom
cstamas:issue-1835

Conversation

@cstamas

@cstamas cstamas commented Apr 16, 2026

Copy link
Copy Markdown
Member

This allows nice version selection. Also, provide a version filter that leaves out preview (sans snapshot) versions.

Finally, made generic classes public, they were package protected.

Fixes #1835

This allows nice version selection. Also, provide a version filter that
leaves out preview (sans snapshot) versions.

Finally, made generic classes public, they were package protected.

Fixes apache#1835
@cstamas cstamas self-assigned this Apr 16, 2026
@cstamas cstamas added the enhancement New feature or request label Apr 16, 2026
@cstamas cstamas added this to the 2.0.17 milestone Apr 16, 2026
@cstamas
cstamas marked this pull request as ready for review April 16, 2026 15:05

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work extracting the qualifier logic into its own public class — the separation of concerns is clean, and the refactoring in GenericVersion.Tokenizer preserves semantic equivalence. A few observations below.

Claude Code on behalf of Guillaume Nodet

// simple case: full qualifier label is present (assuming once)
for (Map.Entry<String, Integer> entry : QUALIFIERS.entrySet()) {
// TODO on map we have empty string "" as key
if (!entry.getKey().isEmpty() && v.contains(entry.getKey())) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The v.contains(entry.getKey()) substring check can produce false positives for short qualifier labels. For example:

  • "1.0-arced" → matches "rc" → incorrectly detected as RC
  • "1.0-legacy" → matches "ga" → incorrectly detected as GA
  • "1.0-sacred" → matches "cr" → incorrectly detected as RC
  • "1.0-dispatcher" → matches "sp" → incorrectly detected as SP

In practice Maven versions are fairly structured so the false positive rate may be low, but since this is a new public API (@since 2.0.17), it might be worth considering a tokenization-based or word-boundary approach (e.g., splitting on ., -, _ first and then doing exact matches on each token — similar to what tokenQualifier already does internally for the parsed path).

What do you think?

QUALIFIERS.put("final", QUALIFIER_ZERO);
QUALIFIERS.put("release", QUALIFIER_ZERO);
QUALIFIERS.put("", QUALIFIER_ZERO); // TODO: is this entry valid?
QUALIFIERS.put("sp", QUALIFIER_SP);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: This TODO was carried over from the original internal code. Now that this class is part of the public API, it might be worth either resolving it (documenting why the empty-string entry exists) or removing it if it's no longer relevant.

GenericQualifiersVersionFilter filter = GenericQualifiersVersionFilter.previewVersionFilter();
assertSame(filter, derive(filter, "g:a:1"));
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filter-level tests are good and cover the main scenarios. It might be valuable to add direct unit tests for GenericQualifiers.qualifier(String) as well — particularly for edge cases like single-character versions, the short-form detection ("a1", "b2", "m3"), and version strings that could trigger the substring false positives mentioned in the other comment.

@cstamas
cstamas requested a review from gnodet April 16, 2026 15:59

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good — clean extraction of qualifier logic into a public API with solid test coverage.

A few non-blocking observations:

  1. Qualifier map type change: TreeMap(CASE_INSENSITIVE_ORDER)LinkedHashMap + explicit toLowerCase() — behaviorally equivalent for ASCII labels, just noting the mechanism changed.
  2. CharsetEncoder in loop (GenericVersionScheme.main()): could be hoisted above the loop, but negligible since it's CLI code.
  3. releasePreviewVersionFilter() naming: might surprise callers who expect snapshots to be filtered — a brief Javadoc note clarifying snapshots are intentionally retained could help.

Nice improvement to assertVersions() diagnostics too.

Claude Code on behalf of Guillaume Nodet

Do not mix snapshots into qualifier filtering. Yes, snapshot
is also a qualifier but is much "bigger" than other
qualifiers (and is much more known). By dropping it, thew
new filter will not deal with them anymore, and will stop
the possible confusion (and there are s and ns filters too).

All in all, this simplifies things: "preview" versions are
already somewhat documented, and they other set is now named
"pre-release" versions (previews including rc/cr).
@cstamas

cstamas commented Apr 17, 2026

Copy link
Copy Markdown
Member Author
  1. releasePreviewVersionFilter() naming: might surprise callers who expect snapshots to be filtered — a brief Javadoc note clarifying snapshots are intentionally retained could help.

Agreed, and dropped 3rd filter that handled snapshots too, in hope to lessen confusion (multiple filters dealing with snapshots), as while snapshots are qualifier, they are also well known by users and well documented in Maven.

Basically kept these two:

  • preview - "alpha", "beta" and "milestone", these are already documented as "preview" versions in Maven doco, also are widely used (see site M releases).
  • pre-release - preview + "rc/cr"; so these qualifiers are all pre-release, and while they do not cover snapshots (another pre-release) for above mentioned reasons, the filter is not dealing with them.

Rename existing PredicateVersionFilter to ArtifactPredicateVersionFilter
to align it with existing VersionPredicateVersionFilter.

Also, UTs were in wrong package (typo).

Finally, fix snapshot and release filters, as they were wrong, they
totally neglected timestamped snapshots, and also dash in
"-SNAPSHOT" is not mandatory.
@cstamas
cstamas merged commit 283b542 into apache:master Apr 17, 2026
14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preview version check should be exposed via API

2 participants