Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package org.eclipse.aether.collection;

import java.util.Collection;
import java.util.EnumMap;
import java.util.Map;

import org.eclipse.aether.graph.Exclusion;
Expand All @@ -29,22 +30,46 @@
* @see DependencyManager#manageDependency(org.eclipse.aether.graph.Dependency)
*/
public final class DependencyManagement {
/**
* Enumeration of manageable attributes, attributes that can be subjected to dependency management.
*
* @since 2.0.17
*/
public enum Subject {
VERSION,
SCOPE,
OPTIONAL,
EXCLUSIONS,
PROPERTIES
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.

Claude Code on behalf of Guillaume Nodet

Replacing typed fields with Map<Subject, Object> loses compile-time type safety. A mistyped managedValues.put(Subject.VERSION, 42) would compile but fail at runtime with a ClassCastException here.

Consider using EnumMap (better memory for enum keys) and keeping typed fields alongside the enforcement tracking, or introducing a small typed record:

private record ManagedValue<T>(T value, boolean enforced) {}

Copy link
Copy Markdown
Member Author

@cstamas cstamas Mar 27, 2026

Choose a reason for hiding this comment

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

Maven Resolver 2.x is still Java 8 baseline, so no record yet. Using EnumMap

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.

Claude Code on behalf of Guillaume Nodet

Good point about the Java 8 baseline. EnumMap is the right choice here. The remaining Map<Subject, Object> type erasure concern is minor given that all setters are type-safe and getters are the only cast points. Acceptable trade-off.

}

private String version;

private String scope;

private Boolean optional;

private Collection<Exclusion> exclusions;

private Map<String, String> properties;
private final EnumMap<Subject, Object> managedValues;
private final EnumMap<Subject, Boolean> managedEnforced;

/**
* Creates an empty management update.
*/
public DependencyManagement() {
// enables default constructor
this.managedValues = new EnumMap<>(Subject.class);
this.managedEnforced = new EnumMap<>(Subject.class);
}

/**
* Returns {@code true} if passed in subject is managed.
*
* @since 2.0.17
*/
public boolean isManagedSubject(Subject subject) {
return managedValues.containsKey(subject);
}

/**
* Returns {@code true} if passed in subject is managed and is enforced.
*
* @since 2.0.17
*/
public boolean isManagedSubjectEnforced(Subject subject) {
return isManagedSubject(subject) && managedEnforced.getOrDefault(subject, false);
}

/**
Expand All @@ -54,17 +79,37 @@ public DependencyManagement() {
* remain unchanged.
*/
public String getVersion() {
return version;
return (String) managedValues.get(Subject.VERSION);
}

/**
* Sets the new version to apply to the dependency.
*
* @param version The new version, may be {@code null} if the version is not managed.
* @return This management update for chaining, never {@code null}.
* @deprecated Use {@link #setVersion(String, boolean)} instead.
*/
@Deprecated
public DependencyManagement setVersion(String version) {
this.version = version;
return setVersion(version, true);
}

/**
* Sets the new version to apply to the dependency.
*
* @param version The new version, may be {@code null} if the version is not managed.
* @param enforced The enforcement of new value.
* @return This management update for chaining, never {@code null}.
* @since 2.0.17
*/
public DependencyManagement setVersion(String version, boolean enforced) {
if (version == null) {
this.managedValues.remove(Subject.VERSION);
this.managedEnforced.remove(Subject.VERSION);
} else {
this.managedValues.put(Subject.VERSION, version);
this.managedEnforced.put(Subject.VERSION, enforced);
}
return this;
}

Expand All @@ -75,17 +120,37 @@ public DependencyManagement setVersion(String version) {
* unchanged.
*/
public String getScope() {
return scope;
return (String) managedValues.get(Subject.SCOPE);
}

/**
* Sets the new scope to apply to the dependency.
*
* @param scope The new scope, may be {@code null} if the scope is not managed.
* @return This management update for chaining, never {@code null}.
* @deprecated Use {@link #setScope(String, boolean)} instead.
*/
@Deprecated
public DependencyManagement setScope(String scope) {
this.scope = scope;
return setScope(scope, true);
}

/**
* Sets the new scope to apply to the dependency.
*
* @param scope The new scope, may be {@code null} if the scope is not managed.
* @param enforced The enforcement of new value.
* @return This management update for chaining, never {@code null}.
* @since 2.0.17
*/
public DependencyManagement setScope(String scope, boolean enforced) {
if (scope == null) {
this.managedValues.remove(Subject.SCOPE);
this.managedEnforced.remove(Subject.SCOPE);
} else {
this.managedValues.put(Subject.SCOPE, scope);
this.managedEnforced.put(Subject.SCOPE, enforced);
}
return this;
}

Expand All @@ -96,17 +161,37 @@ public DependencyManagement setScope(String scope) {
* dependency should remain unchanged.
*/
public Boolean getOptional() {
return optional;
return (Boolean) managedValues.get(Subject.OPTIONAL);
}

/**
* Sets the new optional flag to apply to the dependency.
*
* @param optional The optional flag, may be {@code null} if the flag is not managed.
* @return This management update for chaining, never {@code null}.
* @deprecated Use {@link #setOptional(Boolean, boolean)} instead.
*/
@Deprecated
public DependencyManagement setOptional(Boolean optional) {
this.optional = optional;
return setOptional(optional, true);
}

/**
* Sets the new optional flag to apply to the dependency.
*
* @param optional The optional flag, may be {@code null} if the flag is not managed.
* @param enforced The enforcement of new value.
* @return This management update for chaining, never {@code null}.
* @since 2.0.17
*/
public DependencyManagement setOptional(Boolean optional, boolean enforced) {
if (optional == null) {
this.managedValues.remove(Subject.OPTIONAL);
this.managedEnforced.remove(Subject.OPTIONAL);
} else {
this.managedValues.put(Subject.OPTIONAL, optional);
this.managedEnforced.put(Subject.OPTIONAL, enforced);
}
return this;
}

Expand All @@ -118,8 +203,9 @@ public DependencyManagement setOptional(Boolean optional) {
* @return The new exclusions or {@code null} if the exclusions are not managed and the existing dependency
* exclusions should remain unchanged.
*/
@SuppressWarnings("unchecked")
public Collection<Exclusion> getExclusions() {
return exclusions;
return (Collection<Exclusion>) managedValues.get(Subject.EXCLUSIONS);
}

/**
Expand All @@ -129,9 +215,31 @@ public Collection<Exclusion> getExclusions() {
*
* @param exclusions The new exclusions, may be {@code null} if the exclusions are not managed.
* @return This management update for chaining, never {@code null}.
* @deprecated Use {@link #setExclusions(Collection, boolean)} instead.
*/
@Deprecated
public DependencyManagement setExclusions(Collection<Exclusion> exclusions) {
this.exclusions = exclusions;
return setExclusions(exclusions, true);
}

/**
* Sets the new exclusions to apply to the dependency. Note that this collection denotes the complete set of
* exclusions for the dependency, i.e. the dependency manager controls whether any existing exclusions get merged
* with information from dependency management or overridden by it.
*
* @param exclusions The new exclusions, may be {@code null} if the exclusions are not managed.
* @param enforced The enforcement of new value.
* @return This management update for chaining, never {@code null}.
* @since 2.0.17
*/
public DependencyManagement setExclusions(Collection<Exclusion> exclusions, boolean enforced) {
if (exclusions == null) {
this.managedValues.remove(Subject.EXCLUSIONS);
this.managedEnforced.remove(Subject.EXCLUSIONS);
} else {
this.managedValues.put(Subject.EXCLUSIONS, exclusions);
this.managedEnforced.put(Subject.EXCLUSIONS, enforced);
}
return this;
}

Expand All @@ -143,8 +251,9 @@ public DependencyManagement setExclusions(Collection<Exclusion> exclusions) {
* @return The new artifact properties or {@code null} if the properties are not managed and the existing properties
* should remain unchanged.
*/
@SuppressWarnings("unchecked")
public Map<String, String> getProperties() {
return properties;
return (Map<String, String>) managedValues.get(Subject.PROPERTIES);
}

/**
Expand All @@ -154,9 +263,31 @@ public Map<String, String> getProperties() {
*
* @param properties The new artifact properties, may be {@code null} if the properties are not managed.
* @return This management update for chaining, never {@code null}.
* @deprecated Use {@link #setProperties(Map, boolean)} instead.
*/
@Deprecated
public DependencyManagement setProperties(Map<String, String> properties) {
this.properties = properties;
return setProperties(properties, true);
}

/**
* Sets the new properties to apply to the dependency. Note that this map denotes the complete set of properties,
* i.e. the dependency manager controls whether any existing properties get merged with the information from
* dependency management or overridden by it.
*
* @param properties The new artifact properties, may be {@code null} if the properties are not managed.
* @param enforced The enforcement of new value.
* @return This management update for chaining, never {@code null}.
* @since 2.0.17
*/
public DependencyManagement setProperties(Map<String, String> properties, boolean enforced) {
if (properties == null) {
this.managedValues.remove(Subject.PROPERTIES);
this.managedEnforced.remove(Subject.PROPERTIES);
} else {
this.managedValues.put(Subject.PROPERTIES, properties);
this.managedEnforced.put(Subject.PROPERTIES, enforced);
}
return this;
}
}
Loading