Skip to content

Commit

Permalink
Resolves mojohaus#960: displayDependencyUpdates should display update…
Browse files Browse the repository at this point in the history
…s from lesser segments

Resolves mojohaus#299: allowAnyUpdates should be ignored with a warning message if any of: allowMajorUpdates, allowMinorUpdates, allowIncrementalUpdates is set to false
  • Loading branch information
jarmoniuk committed May 31, 2023
1 parent 0fe821e commit 693fe11
Show file tree
Hide file tree
Showing 37 changed files with 1,109 additions and 607 deletions.
Expand Up @@ -19,19 +19,13 @@
* under the License.
*/

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeSet;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.Restriction;
import org.apache.maven.artifact.versioning.VersionRange;
import org.codehaus.mojo.versions.ordering.BoundArtifactVersion;
Expand Down Expand Up @@ -64,51 +58,93 @@ public abstract class AbstractVersionDetails implements VersionDetails {
*/
private ArtifactVersion currentVersion = null;

private VersionRange currentVersionRange = null;

protected boolean verboseDetail = true;

protected AbstractVersionDetails() {}

@Override
public Restriction restrictionFor(Optional<Segment> scope) throws InvalidSegmentException {
// one range spec can have multiple restrictions, and multiple 'lower bound', we want the highest one.
// [1.0,2.0),[3.0,4.0) -> 3.0
ArtifactVersion highestLowerBound = currentVersion;
if (currentVersion != null) {
try {
highestLowerBound =
VersionRange.createFromVersionSpec(currentVersion.toString()).getRestrictions().stream()
.map(Restriction::getLowerBound)
.filter(Objects::nonNull)
.max(getVersionComparator())
.orElse(currentVersion);
} catch (InvalidVersionSpecificationException ignored) {
ignored.printStackTrace(System.err);
}
}
/**
* If a version is a version range consisting of one or more version ranges, returns the highest <u>lower</u>
* bound. If a single version range is present, returns its value.
* @param lowerBoundVersion actual version used
* @return highest lower bound of the given version range or {@link #getCurrentVersion()} if there's no lower bound
*/
protected ArtifactVersion getHighestLowerBound(ArtifactVersion lowerBoundVersion) {
return getCurrentVersionRange().getRestrictions().stream()
.map(Restriction::getLowerBound)
.filter(Objects::nonNull)
.max(getVersionComparator())
.orElse(lowerBoundVersion);
}

final ArtifactVersion currentVersion = highestLowerBound;
ArtifactVersion nextVersion = scope.filter(s -> s.isMajorTo(SUBINCREMENTAL))
.map(s -> (ArtifactVersion) new BoundArtifactVersion(currentVersion, Segment.of(s.value() + 1)))
.orElse(currentVersion);
/**
* If the artifact is bound by one or more version ranges, returns the restriction that constitutes
* the version range containing the selected actual version.
* If there are no version ranges, returns the provided version.
* @param selectedVersion actual version used
* @return restriction containing the version range selected by the given version,
* or {@link Optional#empty()} if there are no ranges
*/
protected Optional<Restriction> getSelectedRestriction(ArtifactVersion selectedVersion) {
return Optional.ofNullable(getCurrentVersionRange())
.map(VersionRange::getRestrictions)
.flatMap(r -> r.stream()
.filter(rr -> rr.containsVersion(selectedVersion))
.findAny());
}

@Override
public Restriction restrictionForSelectedSegment(ArtifactVersion lowerBound, Optional<Segment> selectedSegment) {
ArtifactVersion highestLowerBound = getHighestLowerBound(lowerBound);
ArtifactVersion nextVersion = selectedSegment
.filter(s -> s.isMajorTo(SUBINCREMENTAL))
.map(Segment::minorTo)
.map(s -> (ArtifactVersion) new BoundArtifactVersion(highestLowerBound, s))
.orElse(highestLowerBound);
return new Restriction(
nextVersion,
false,
scope.filter(MAJOR::isMajorTo)
.map(s -> (ArtifactVersion) new BoundArtifactVersion(currentVersion, s))
selectedSegment
.filter(MAJOR::isMajorTo)
.map(s -> (ArtifactVersion) new BoundArtifactVersion(highestLowerBound, s))
.orElse(null),
false);
}

@Override
public Restriction restrictionForIgnoreScope(Optional<Segment> ignored) {
ArtifactVersion nextVersion = ignored.map(s -> (ArtifactVersion) new BoundArtifactVersion(currentVersion, s))
.orElse(currentVersion);
return new Restriction(nextVersion, false, null, false);
public Restriction restrictionForUnchangedSegment(
ArtifactVersion actualVersion, Optional<Segment> unchangedSegment, boolean allowDowngrade)
throws InvalidSegmentException {
Optional<Restriction> selectedRestriction = getSelectedRestriction(actualVersion);
ArtifactVersion selectedRestrictionUpperBound =
selectedRestriction.map(Restriction::getUpperBound).orElse(actualVersion);
ArtifactVersion lowerBound = allowDowngrade
? getLowerBound(selectedRestrictionUpperBound, unchangedSegment)
.map(DefaultArtifactVersionCache::of)
.orElse(null)
: selectedRestrictionUpperBound;
ArtifactVersion upperBound = unchangedSegment
.map(s -> (ArtifactVersion) new BoundArtifactVersion(
selectedRestrictionUpperBound, s.isMajorTo(SUBINCREMENTAL) ? Segment.minorTo(s) : s))
.orElse(null);
return new Restriction(
lowerBound,
allowDowngrade
|| selectedRestriction
.map(Restriction::isUpperBoundInclusive)
.map(b -> !b)
.orElse(false),
upperBound,
allowDowngrade);
}

@Override
public final boolean isCurrentVersionDefined() {
return getCurrentVersion() != null;
public Restriction restrictionForIgnoreScope(ArtifactVersion lowerBound, Optional<Segment> ignored) {
ArtifactVersion highestLowerBound = getHighestLowerBound(lowerBound);
ArtifactVersion nextVersion = ignored.map(s -> (ArtifactVersion) new BoundArtifactVersion(highestLowerBound, s))
.orElse(highestLowerBound);
return new Restriction(nextVersion, false, null, false);
}

@Override
Expand All @@ -122,27 +158,23 @@ public final void setCurrentVersion(ArtifactVersion currentVersion) {
}

@Override
public final void setCurrentVersion(String currentVersion) {
setCurrentVersion(currentVersion == null ? null : DefaultArtifactVersionCache.of(currentVersion));
public final VersionRange getCurrentVersionRange() {
return currentVersionRange;
}

@Override
public final ArtifactVersion[] getVersions(VersionRange versionRange, boolean includeSnapshots) {
return getVersions(versionRange, null, includeSnapshots);
public final void setCurrentVersionRange(VersionRange versionRange) {
currentVersionRange = versionRange;
}

@Override
public final ArtifactVersion[] getVersions(
ArtifactVersion lowerBound, ArtifactVersion upperBound, boolean includeSnapshots) {
Restriction restriction = new Restriction(lowerBound, false, upperBound, false);
return getVersions(restriction, includeSnapshots);
public final void setCurrentVersion(String currentVersion) {
setCurrentVersion(currentVersion == null ? null : DefaultArtifactVersionCache.of(currentVersion));
}

@Override
public final ArtifactVersion getNewestVersion(
ArtifactVersion lowerBound, ArtifactVersion upperBound, boolean includeSnapshots) {
Restriction restriction = new Restriction(lowerBound, false, upperBound, false);
return getNewestVersion(restriction, includeSnapshots);
public final ArtifactVersion[] getVersions(VersionRange versionRange, boolean includeSnapshots) {
return getVersions(versionRange, null, includeSnapshots);
}

@Override
Expand Down Expand Up @@ -187,24 +219,6 @@ public final boolean containsVersion(String version) {
return false;
}

private ArtifactVersion[] getNewerVersions(ArtifactVersion version, boolean includeSnapshots) {
Restriction restriction = new Restriction(version, false, null, false);
return getVersions(restriction, includeSnapshots);
}

@Override
public final ArtifactVersion[] getNewerVersions(String version, boolean includeSnapshots) {
return getNewerVersions(DefaultArtifactVersionCache.of(version), includeSnapshots);
}

@Deprecated
@Override
public final ArtifactVersion[] getNewerVersions(
String version, Optional<Segment> upperBoundSegment, boolean includeSnapshots)
throws InvalidSegmentException {
return getNewerVersions(version, upperBoundSegment, includeSnapshots, false);
}

@Override
public final ArtifactVersion[] getNewerVersions(
String versionString, Optional<Segment> unchangedSegment, boolean includeSnapshots, boolean allowDowngrade)
Expand All @@ -216,8 +230,8 @@ public final ArtifactVersion[] getNewerVersions(
.orElse(null)
: currentVersion;
ArtifactVersion upperBound = unchangedSegment
.map(s -> (ArtifactVersion) new BoundArtifactVersion(
currentVersion, s.isMajorTo(SUBINCREMENTAL) ? Segment.of(s.value() + 1) : s))
.map(s -> (ArtifactVersion)
new BoundArtifactVersion(currentVersion, s.isMajorTo(SUBINCREMENTAL) ? Segment.minorTo(s) : s))
.orElse(null);

Restriction restriction = new Restriction(lowerBound, allowDowngrade, upperBound, allowDowngrade);
Expand All @@ -226,22 +240,21 @@ public final ArtifactVersion[] getNewerVersions(

@Override
public Optional<ArtifactVersion> getNewestVersion(
String versionString, Optional<Segment> upperBoundSegment, boolean includeSnapshots, boolean allowDowngrade)
String actualVersion, Optional<Segment> unchangedSegment, boolean includeSnapshots, boolean allowDowngrade)
throws InvalidSegmentException {
ArtifactVersion currentVersion = DefaultArtifactVersionCache.of(versionString);
ArtifactVersion lowerBound = allowDowngrade
? getLowerBound(currentVersion, upperBoundSegment)
.map(DefaultArtifactVersionCache::of)
.orElse(null)
: currentVersion;
ArtifactVersion upperBound = upperBoundSegment
.map(s -> (ArtifactVersion) new BoundArtifactVersion(
currentVersion, s.isMajorTo(SUBINCREMENTAL) ? Segment.of(s.value() + 1) : s))
.orElse(null);

Restriction restriction = new Restriction(lowerBound, allowDowngrade, upperBound, allowDowngrade);
Restriction segmentRestriction = restrictionForUnchangedSegment(
DefaultArtifactVersionCache.of(actualVersion), unchangedSegment, allowDowngrade);
Restriction lookupRestriction;
if (!allowDowngrade
&& Optional.ofNullable(currentVersion)
.map(v -> v.compareTo(segmentRestriction.getLowerBound()) > 0)
.orElse(false)) {
lookupRestriction = new Restriction(currentVersion, false, null, false);
} else {
lookupRestriction = segmentRestriction;
}
return Arrays.stream(getVersions(includeSnapshots))
.filter(candidate -> isVersionInRestriction(restriction, candidate))
.filter(candidate -> isVersionInRestriction(lookupRestriction, candidate))
.filter(candidate -> includeSnapshots || !ArtifactUtils.isSnapshot(candidate.toString()))
.max(getVersionComparator());
}
Expand All @@ -264,36 +277,28 @@ public final ArtifactVersion[] getVersions(
}

@Override
public final ArtifactVersion getNewestUpdate(
public final ArtifactVersion getNewestUpdateWithinSegment(
ArtifactVersion currentVersion, Optional<Segment> updateScope, boolean includeSnapshots) {
try {
return getNewestVersion(restrictionFor(updateScope), includeSnapshots);
} catch (InvalidSegmentException e) {
return null;
}
return getNewestVersion(restrictionForSelectedSegment(currentVersion, updateScope), includeSnapshots);
}

@Override
public final ArtifactVersion[] getAllUpdates(
ArtifactVersion currentVersion, Optional<Segment> updateScope, boolean includeSnapshots) {
try {
return getVersions(restrictionFor(updateScope), includeSnapshots);
} catch (InvalidSegmentException e) {
return null;
}
return getVersions(restrictionForSelectedSegment(currentVersion, updateScope), includeSnapshots);
}

@Override
public final ArtifactVersion getNewestUpdate(Optional<Segment> updateScope, boolean includeSnapshots) {
if (isCurrentVersionDefined()) {
return getNewestUpdate(getCurrentVersion(), updateScope, includeSnapshots);
public final ArtifactVersion getNewestUpdateWithinSegment(Optional<Segment> updateScope, boolean includeSnapshots) {
if (getCurrentVersion() != null) {
return getNewestUpdateWithinSegment(getCurrentVersion(), updateScope, includeSnapshots);
}
return null;
}

@Override
public final ArtifactVersion[] getAllUpdates(Optional<Segment> updateScope, boolean includeSnapshots) {
if (isCurrentVersionDefined()) {
if (getCurrentVersion() != null) {
return getAllUpdates(getCurrentVersion(), updateScope, includeSnapshots);
}
return null;
Expand Down Expand Up @@ -433,15 +438,11 @@ public final ArtifactVersion[] getReportUpdates(Optional<Segment> updateScope, b
* @return all versions after currentVersion within the specified update scope.
*/
private Stream<ArtifactVersion> getArtifactVersionStream(Optional<Segment> updateScope, boolean includeSnapshots) {
if (isCurrentVersionDefined()) {
try {
Restriction restriction = restrictionFor(updateScope);

return Arrays.stream(getVersions(includeSnapshots))
.filter(candidate -> isVersionInRestriction(restriction, candidate));
} catch (InvalidSegmentException ignored) {
ignored.printStackTrace(System.err);
}
if (getCurrentVersion() != null) {
Restriction restriction = restrictionForSelectedSegment(getCurrentVersion(), updateScope);

return Arrays.stream(getVersions(includeSnapshots))
.filter(candidate -> isVersionInRestriction(restriction, candidate));
}
return Stream.empty();
}
Expand Down
Expand Up @@ -75,12 +75,8 @@ public ArtifactVersions(Artifact artifact, List<ArtifactVersion> versions, Versi
this.versionComparator = versionComparator;
this.versions = new TreeSet<>(versionComparator);
this.versions.addAll(versions);
// DefaultArtifact objects are often built from raw model, without a version set
// (where the actual version is taken from parent or dependency/plugin management)
// this probably isn't the case in an actual Maven execution
if (artifact.getVersion() != null) {
setCurrentVersion(artifact.getVersion());
}
setCurrentVersion(artifact.getVersion());
setCurrentVersionRange(artifact.getVersionRange());
}

/**
Expand All @@ -94,6 +90,7 @@ public ArtifactVersions(ArtifactVersions other) {
versionComparator = other.versionComparator;
versions = other.versions;
setCurrentVersion(other.getCurrentVersion());
setCurrentVersionRange(other.getCurrentVersionRange());
}

@SuppressWarnings("checkstyle:InnerAssignment")
Expand Down
Expand Up @@ -27,7 +27,7 @@
import org.apache.commons.lang3.tuple.Triple;

/**
* Utility providing a cached {@link ArtifactVersions#getNewestUpdate(Optional, boolean)} API
* Utility providing a cached {@link ArtifactVersions#getNewestUpdateWithinSegment(Optional, boolean)} API
*/
public class ArtifactVersionsCache {
private TriFunction<AbstractVersionDetails, Optional<Segment>, Boolean, ?> cachedFunction;
Expand Down
Expand Up @@ -53,6 +53,7 @@
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.Restriction;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.execution.MavenSession;
Expand Down Expand Up @@ -688,11 +689,10 @@ public Map<Property, PropertyVersions> getVersionPropertiesMap(VersionProperties
if (dependencies != null) {
for (Dependency dependency : dependencies) {
getLog().debug("Property ${" + property.getName() + "}: Adding association to " + dependency);
builder.addAssociation(this.createDependencyArtifact(dependency), false);
builder.withAssociation(this.createDependencyArtifact(dependency), false);
}
}
try {
final PropertyVersions versions = builder.newPropertyVersions();
if (property.isAutoLinkDependencies()
&& StringUtils.isEmpty(property.getVersion())
&& !StringUtils.isEmpty(builder.getVersionRange())) {
Expand All @@ -702,8 +702,17 @@ public Map<Property, PropertyVersions> getVersionPropertiesMap(VersionProperties
}
final String currentVersion =
request.getMavenProject().getProperties().getProperty(property.getName());
versions.setCurrentVersion(currentVersion);
property.setValue(currentVersion);
final PropertyVersions versions;
try {
if (currentVersion != null) {
builder.withCurrentVersion(DefaultArtifactVersionCache.of(currentVersion))
.withCurrentVersionRange(VersionRange.createFromVersionSpec(currentVersion));
}
} catch (InvalidVersionSpecificationException e) {
throw new RuntimeException(e);
}
versions = builder.build();
propertyVersions.put(property, versions);
} catch (VersionRetrievalException e) {
throw new MojoExecutionException(e.getMessage(), e);
Expand Down

0 comments on commit 693fe11

Please sign in to comment.