diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java index 71bfd5102..23652eedd 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/DependencyManagement.java @@ -19,6 +19,7 @@ package org.eclipse.aether.collection; import java.util.Collection; +import java.util.HashMap; import java.util.Map; import org.eclipse.aether.graph.Exclusion; @@ -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.14 + */ + public enum Subject { + VERSION, + SCOPE, + OPTIONAL, + EXCLUSIONS, + PROPERTIES + } - private String version; - - private String scope; - - private Boolean optional; - - private Collection exclusions; - - private Map properties; + private final Map managedValues; + private final Map managedEnforced; /** * Creates an empty management update. */ public DependencyManagement() { - // enables default constructor + this.managedValues = new HashMap<>(); + this.managedEnforced = new HashMap<>(); + } + + /** + * Returns {@code true} if passed in subject is managed. + * + * @since 2.0.14 + */ + public boolean isManagedSubject(Subject subject) { + return managedValues.containsKey(subject); + } + + /** + * Returns {@code true} if passed in subject is managed and is enforced. + * + * @since 2.0.14 + */ + public boolean isManagedSubjectEnforced(Subject subject) { + return isManagedSubject(subject) && managedEnforced.getOrDefault(subject, false); } /** @@ -54,7 +79,7 @@ public DependencyManagement() { * remain unchanged. */ public String getVersion() { - return version; + return (String) managedValues.get(Subject.VERSION); } /** @@ -62,9 +87,29 @@ public String getVersion() { * * @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.14 + */ + 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; } @@ -75,7 +120,7 @@ public DependencyManagement setVersion(String version) { * unchanged. */ public String getScope() { - return scope; + return (String) managedValues.get(Subject.SCOPE); } /** @@ -83,9 +128,29 @@ public String getScope() { * * @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.14 + */ + 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; } @@ -96,7 +161,7 @@ public DependencyManagement setScope(String scope) { * dependency should remain unchanged. */ public Boolean getOptional() { - return optional; + return (Boolean) managedValues.get(Subject.OPTIONAL); } /** @@ -104,9 +169,29 @@ public Boolean getOptional() { * * @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.14 + */ + 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; } @@ -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 getExclusions() { - return exclusions; + return (Collection) managedValues.get(Subject.EXCLUSIONS); } /** @@ -129,9 +215,31 @@ public Collection 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 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.14 + */ + public DependencyManagement setExclusions(Collection 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; } @@ -143,8 +251,9 @@ public DependencyManagement setExclusions(Collection 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 getProperties() { - return properties; + return (Map) managedValues.get(Subject.PROPERTIES); } /** @@ -154,9 +263,31 @@ public Map 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 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.14 + */ + public DependencyManagement setProperties(Map 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; } } diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DefaultDependencyNode.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DefaultDependencyNode.java index 3d7c4fde0..fdfdddf64 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DefaultDependencyNode.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DefaultDependencyNode.java @@ -26,6 +26,7 @@ import java.util.Map; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.collection.DependencyManagement; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.version.Version; import org.eclipse.aether.version.VersionConstraint; @@ -51,7 +52,7 @@ public final class DefaultDependencyNode implements DependencyNode { private Version version; - private byte managedBits; + private Map managedSubjects = Collections.emptyMap(); private List repositories; @@ -104,7 +105,19 @@ public DefaultDependencyNode(DependencyNode node) { children = new ArrayList<>(0); setAliases(node.getAliases()); setRequestContext(node.getRequestContext()); - setManagedBits(node.getManagedBits()); + + HashMap managedSubjects = new HashMap<>(); + for (DependencyManagement.Subject subject : DependencyManagement.Subject.values()) { + if (node.isManagedSubject(subject)) { + managedSubjects.put(subject, node.isManagedSubjectEnforced(subject)); + } + } + if (managedSubjects.isEmpty()) { + setManagedSubjects(null); + } else { + setManagedSubjects(managedSubjects); + } + setRelocations(node.getRelocations()); setRepositories(node.getRepositories()); setVersion(node.getVersion()); @@ -113,10 +126,12 @@ public DefaultDependencyNode(DependencyNode node) { setData(data.isEmpty() ? null : new HashMap<>(data)); } + @Override public List getChildren() { return children; } + @Override public void setChildren(List children) { if (children == null) { this.children = new ArrayList<>(0); @@ -125,14 +140,17 @@ public void setChildren(List children) { } } + @Override public Dependency getDependency() { return dependency; } + @Override public Artifact getArtifact() { return artifact; } + @Override public void setArtifact(Artifact artifact) { if (dependency == null) { throw new IllegalStateException("node does not have a dependency"); @@ -141,6 +159,7 @@ public void setArtifact(Artifact artifact) { this.artifact = dependency.getArtifact(); } + @Override public List getRelocations() { return relocations; } @@ -158,6 +177,7 @@ public void setRelocations(List relocations) { } } + @Override public Collection getAliases() { return aliases; } @@ -175,6 +195,7 @@ public void setAliases(Collection aliases) { } } + @Override public VersionConstraint getVersionConstraint() { return versionConstraint; } @@ -188,6 +209,7 @@ public void setVersionConstraint(VersionConstraint versionConstraint) { this.versionConstraint = versionConstraint; } + @Override public Version getVersion() { return version; } @@ -201,6 +223,7 @@ public void setVersion(Version version) { this.version = version; } + @Override public void setScope(String scope) { if (dependency == null) { throw new IllegalStateException("node does not have a dependency"); @@ -208,6 +231,7 @@ public void setScope(String scope) { dependency = dependency.setScope(scope); } + @Override public void setOptional(Boolean optional) { if (dependency == null) { throw new IllegalStateException("node does not have a dependency"); @@ -215,20 +239,51 @@ public void setOptional(Boolean optional) { dependency = dependency.setOptional(optional); } + @Override public int getManagedBits() { - return managedBits; + byte res = 0; + if (isManagedSubject(DependencyManagement.Subject.VERSION)) { + res |= DependencyNode.MANAGED_VERSION; + } + if (isManagedSubject(DependencyManagement.Subject.SCOPE)) { + res |= DependencyNode.MANAGED_SCOPE; + } + if (isManagedSubject(DependencyManagement.Subject.OPTIONAL)) { + res |= DependencyNode.MANAGED_OPTIONAL; + } + if (isManagedSubject(DependencyManagement.Subject.PROPERTIES)) { + res |= DependencyNode.MANAGED_PROPERTIES; + } + if (isManagedSubject(DependencyManagement.Subject.EXCLUSIONS)) { + res |= DependencyNode.MANAGED_EXCLUSIONS; + } + return res; } - /** - * Sets a bit field indicating which attributes of this node were subject to dependency management. - * - * @param managedBits The bit field indicating the managed attributes or {@code 0} if dependency management wasn't - * applied. - */ + @Deprecated public void setManagedBits(int managedBits) { - this.managedBits = (byte) (managedBits & 0x1F); + throw new IllegalArgumentException("bits are not supported"); + } + + public void setManagedSubjects(Map managedSubjects) { + if (managedSubjects == null) { + this.managedSubjects = Collections.emptyMap(); + } else { + this.managedSubjects = managedSubjects; + } + } + + @Override + public boolean isManagedSubject(DependencyManagement.Subject subject) { + return managedSubjects.containsKey(subject); } + @Override + public boolean isManagedSubjectEnforced(DependencyManagement.Subject subject) { + return managedSubjects.getOrDefault(subject, false); + } + + @Override public List getRepositories() { return repositories; } @@ -246,18 +301,22 @@ public void setRepositories(List repositories) { } } + @Override public String getRequestContext() { return context; } + @Override public void setRequestContext(String context) { this.context = (context != null) ? context.intern() : ""; } + @Override public Map getData() { return data; } + @Override public void setData(Map data) { if (data == null) { this.data = Collections.emptyMap(); @@ -266,6 +325,7 @@ public void setData(Map data) { } } + @Override public void setData(Object key, Object value) { requireNonNull(key, "key cannot be null"); @@ -285,6 +345,7 @@ public void setData(Object key, Object value) { } } + @Override public boolean accept(DependencyVisitor visitor) { if (Thread.currentThread().isInterrupted()) { throw new RuntimeException(new InterruptedException("Thread interrupted")); diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyNode.java b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyNode.java index 747163985..8a809d4e1 100644 --- a/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyNode.java +++ b/maven-resolver-api/src/main/java/org/eclipse/aether/graph/DependencyNode.java @@ -23,6 +23,7 @@ import java.util.Map; import org.eclipse.aether.artifact.Artifact; +import org.eclipse.aether.collection.DependencyManagement; import org.eclipse.aether.repository.RemoteRepository; import org.eclipse.aether.version.Version; import org.eclipse.aether.version.VersionConstraint; @@ -38,40 +39,49 @@ * @noextend This interface is not intended to be extended by clients. */ public interface DependencyNode { - /** * A bit flag indicating the dependency version was subject to dependency management * * @see #getManagedBits() + * @deprecated Use {@link #isManagedSubject(DependencyManagement.Subject)} and {@link #isManagedSubjectEnforced(DependencyManagement.Subject)} instead. */ + @Deprecated int MANAGED_VERSION = 0x01; /** * A bit flag indicating the dependency scope was subject to dependency management * * @see #getManagedBits() + * @deprecated Use {@link #isManagedSubject(DependencyManagement.Subject)} and {@link #isManagedSubjectEnforced(DependencyManagement.Subject)} instead. */ + @Deprecated int MANAGED_SCOPE = 0x02; /** * A bit flag indicating the optional flag was subject to dependency management * * @see #getManagedBits() + * @deprecated Use {@link #isManagedSubject(DependencyManagement.Subject)} and {@link #isManagedSubjectEnforced(DependencyManagement.Subject)} instead. */ + @Deprecated int MANAGED_OPTIONAL = 0x04; /** * A bit flag indicating the artifact properties were subject to dependency management * * @see #getManagedBits() + * @deprecated Use {@link #isManagedSubject(DependencyManagement.Subject)} and {@link #isManagedSubjectEnforced(DependencyManagement.Subject)} instead. */ + @Deprecated int MANAGED_PROPERTIES = 0x08; /** * A bit flag indicating the exclusions were subject to dependency management * * @see #getManagedBits() + * @deprecated Use {@link #isManagedSubject(DependencyManagement.Subject)} and {@link #isManagedSubjectEnforced(DependencyManagement.Subject)} instead. */ + @Deprecated int MANAGED_EXCLUSIONS = 0x10; /** @@ -170,9 +180,27 @@ public interface DependencyNode { * @return A bit field containing any of the bits {@link #MANAGED_VERSION}, {@link #MANAGED_SCOPE}, * {@link #MANAGED_OPTIONAL}, {@link #MANAGED_PROPERTIES} and {@link #MANAGED_EXCLUSIONS} if the * corresponding attribute was set via dependency management. + * @deprecated Use {@link #isManagedSubject(DependencyManagement.Subject)} and {@link #isManagedSubjectEnforced(DependencyManagement.Subject)} instead. */ + @Deprecated int getManagedBits(); + /** + * Returns {@code true} if given subject is managed. + * + * @see org.eclipse.aether.collection.DependencyManagement.Subject + * @since 2.0.14 + */ + boolean isManagedSubject(DependencyManagement.Subject subject); + + /** + * Returns {@code true} if given subject is managed with enforcing modality on this node. + * + * @see org.eclipse.aether.collection.DependencyManagement.Subject + * @since 2.0.14 + */ + boolean isManagedSubjectEnforced(DependencyManagement.Subject subject); + /** * Gets the remote repositories from which this node's artifact shall be resolved. * diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/PremanagedDependency.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/PremanagedDependency.java index c0a6ef3bb..f443f51c3 100644 --- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/PremanagedDependency.java +++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/PremanagedDependency.java @@ -29,7 +29,6 @@ import org.eclipse.aether.collection.DependencyManager; import org.eclipse.aether.graph.DefaultDependencyNode; import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.graph.Exclusion; import org.eclipse.aether.util.graph.manager.DependencyManagerUtils; @@ -54,7 +53,10 @@ public class PremanagedDependency { */ final Map premanagedProperties; - final int managedBits; + /** + * @since 2.0.14 + */ + final Map managedSubjects; final Dependency managedDependency; @@ -67,7 +69,7 @@ public class PremanagedDependency { Boolean premanagedOptional, Collection premanagedExclusions, Map premanagedProperties, - int managedBits, + Map managedSubjects, Dependency managedDependency, boolean premanagedState) { this.premanagedVersion = premanagedVersion; @@ -80,7 +82,7 @@ public class PremanagedDependency { this.premanagedProperties = premanagedProperties != null ? Collections.unmodifiableMap(new HashMap<>(premanagedProperties)) : null; - this.managedBits = managedBits; + this.managedSubjects = managedSubjects; this.managedDependency = managedDependency; this.premanagedState = premanagedState; } @@ -92,7 +94,7 @@ public static PremanagedDependency create( boolean premanagedState) { DependencyManagement depMngt = depManager != null ? depManager.manageDependency(dependency) : null; - int managedBits = 0; + Map managedSubjects = new HashMap<>(); String premanagedVersion = null; String premanagedScope = null; Boolean premanagedOptional = null; @@ -104,37 +106,48 @@ public static PremanagedDependency create( Artifact artifact = dependency.getArtifact(); premanagedVersion = artifact.getVersion(); dependency = dependency.setArtifact(artifact.setVersion(depMngt.getVersion())); - managedBits |= DependencyNode.MANAGED_VERSION; + managedSubjects.put( + DependencyManagement.Subject.VERSION, + depMngt.isManagedSubjectEnforced(DependencyManagement.Subject.VERSION)); } if (depMngt.getProperties() != null) { Artifact artifact = dependency.getArtifact(); premanagedProperties = artifact.getProperties(); dependency = dependency.setArtifact(artifact.setProperties(depMngt.getProperties())); - managedBits |= DependencyNode.MANAGED_PROPERTIES; + managedSubjects.put( + DependencyManagement.Subject.PROPERTIES, + depMngt.isManagedSubjectEnforced(DependencyManagement.Subject.PROPERTIES)); } if (depMngt.getScope() != null) { premanagedScope = dependency.getScope(); dependency = dependency.setScope(depMngt.getScope()); - managedBits |= DependencyNode.MANAGED_SCOPE; + managedSubjects.put( + DependencyManagement.Subject.SCOPE, + depMngt.isManagedSubjectEnforced(DependencyManagement.Subject.SCOPE)); } if (depMngt.getOptional() != null) { premanagedOptional = dependency.isOptional(); dependency = dependency.setOptional(depMngt.getOptional()); - managedBits |= DependencyNode.MANAGED_OPTIONAL; + managedSubjects.put( + DependencyManagement.Subject.OPTIONAL, + depMngt.isManagedSubjectEnforced(DependencyManagement.Subject.OPTIONAL)); } if (depMngt.getExclusions() != null) { premanagedExclusions = dependency.getExclusions(); dependency = dependency.setExclusions(depMngt.getExclusions()); - managedBits |= DependencyNode.MANAGED_EXCLUSIONS; + managedSubjects.put( + DependencyManagement.Subject.EXCLUSIONS, + depMngt.isManagedSubjectEnforced(DependencyManagement.Subject.EXCLUSIONS)); } } + return new PremanagedDependency( premanagedVersion, premanagedScope, premanagedOptional, premanagedExclusions, premanagedProperties, - managedBits, + managedSubjects, dependency, premanagedState); } @@ -144,7 +157,7 @@ public Dependency getManagedDependency() { } public void applyTo(DefaultDependencyNode child) { - child.setManagedBits(managedBits); + child.setManagedSubjects(managedSubjects); if (premanagedState) { child.setData(DependencyManagerUtils.NODE_DATA_PREMANAGED_VERSION, premanagedVersion); child.setData(DependencyManagerUtils.NODE_DATA_PREMANAGED_SCOPE, premanagedScope); diff --git a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/DependencyGraphParser.java b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/DependencyGraphParser.java index 18da45bb7..944808104 100644 --- a/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/DependencyGraphParser.java +++ b/maven-resolver-test-util/src/main/java/org/eclipse/aether/internal/test/util/DependencyGraphParser.java @@ -36,6 +36,7 @@ import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.collection.DependencyManagement; import org.eclipse.aether.graph.DefaultDependencyNode; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyNode; @@ -291,16 +292,16 @@ private DependencyNode build(DependencyNode parent, LineContext ctx, boolean isR DefaultArtifact artifact = new DefaultArtifact(def.coords, def.properties); Dependency dependency = new Dependency(artifact, def.scope, def.optional); node = new DefaultDependencyNode(dependency); - int managedBits = 0; + Map managedSubjects = new HashMap<>(); if (def.premanagedScope != null) { - managedBits |= DependencyNode.MANAGED_SCOPE; + managedSubjects.put(DependencyManagement.Subject.SCOPE, true); node.setData("premanaged.scope", def.premanagedScope); } if (def.premanagedVersion != null) { - managedBits |= DependencyNode.MANAGED_VERSION; + managedSubjects.put(DependencyManagement.Subject.VERSION, true); node.setData("premanaged.version", def.premanagedVersion); } - node.setManagedBits(managedBits); + node.setManagedSubjects(managedSubjects); if (def.relocations != null) { List relocations = new ArrayList<>(); for (String relocation : def.relocations) { diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/AbstractDependencyManager.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/AbstractDependencyManager.java index 556b9c588..6c933a382 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/AbstractDependencyManager.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/AbstractDependencyManager.java @@ -86,9 +86,10 @@ *

Managed Bits and Graph Transformations

*

* When a {@link org.eclipse.aether.graph.DependencyNode} becomes "managed" by any property - * provided from this manager, {@link org.eclipse.aether.graph.DependencyNode#getManagedBits()} + * provided from this manager, {@link org.eclipse.aether.graph.DependencyNode#isManagedSubject(DependencyManagementSubject)} + * and {@link org.eclipse.aether.graph.DependencyNode#isManagedSubjectEnforced(DependencyManagementSubject)} * will carry this information for the given property. Later graph transformations will abstain - * from modifying these properties of marked nodes (assuming the node already has the property + * from modifying these properties of marked enforced nodes (assuming the node already has the property * set to what it should have). Sometimes this is unwanted, especially for properties that need * to be inherited in the graph (values derived from parent-child context of the actual node, * like "scope" or "optional"). @@ -206,14 +207,14 @@ private boolean containsManagedVersion(Key key) { return managedVersions != null && managedVersions.containsKey(key); } - private String getManagedVersion(Key key) { + private AbstractDependencyManager getManagedVersion(Key key) { for (AbstractDependencyManager ancestor : path) { if (ancestor.managedVersions != null && ancestor.managedVersions.containsKey(key)) { - return ancestor.managedVersions.get(key); + return ancestor; } } if (depth == 1 && managedVersions != null && managedVersions.containsKey(key)) { - return managedVersions.get(key); + return this; } return null; } @@ -227,14 +228,14 @@ private boolean containsManagedScope(Key key) { return managedScopes != null && managedScopes.containsKey(key); } - private String getManagedScope(Key key) { + private AbstractDependencyManager getManagedScope(Key key) { for (AbstractDependencyManager ancestor : path) { if (ancestor.managedScopes != null && ancestor.managedScopes.containsKey(key)) { - return ancestor.managedScopes.get(key); + return ancestor; } } if (depth == 1 && managedScopes != null && managedScopes.containsKey(key)) { - return managedScopes.get(key); + return this; } return null; } @@ -248,14 +249,14 @@ private boolean containsManagedOptional(Key key) { return managedOptionals != null && managedOptionals.containsKey(key); } - private Boolean getManagedOptional(Key key) { + private AbstractDependencyManager getManagedOptional(Key key) { for (AbstractDependencyManager ancestor : path) { if (ancestor.managedOptionals != null && ancestor.managedOptionals.containsKey(key)) { - return ancestor.managedOptionals.get(key); + return ancestor; } } if (depth == 1 && managedOptionals != null && managedOptionals.containsKey(key)) { - return managedOptionals.get(key); + return this; } return null; } @@ -276,14 +277,14 @@ private boolean containsManagedLocalPath(Key key) { * @param key the dependency key * @return the managed local path, or null if not managed */ - private String getManagedLocalPath(Key key) { + private AbstractDependencyManager getManagedLocalPath(Key key) { for (AbstractDependencyManager ancestor : path) { if (ancestor.managedLocalPaths != null && ancestor.managedLocalPaths.containsKey(key)) { - return ancestor.managedLocalPaths.get(key); + return ancestor; } } if (managedLocalPaths != null && managedLocalPaths.containsKey(key)) { - return managedLocalPaths.get(key); + return this; } return null; } @@ -334,22 +335,20 @@ public DependencyManager deriveChildManager(DependencyCollectionContext context) managedVersions.put(key, version); } - if (isInheritedDerived()) { - String scope = managedDependency.getScope(); - if (!scope.isEmpty() && !containsManagedScope(key)) { - if (managedScopes == null) { - managedScopes = MMap.emptyNotDone(); - } - managedScopes.put(key, scope); + String scope = managedDependency.getScope(); + if (!scope.isEmpty() && !containsManagedScope(key)) { + if (managedScopes == null) { + managedScopes = MMap.emptyNotDone(); } + managedScopes.put(key, scope); + } - Boolean optional = managedDependency.getOptional(); - if (optional != null && !containsManagedOptional(key)) { - if (managedOptionals == null) { - managedOptionals = MMap.emptyNotDone(); - } - managedOptionals.put(key, optional); + Boolean optional = managedDependency.getOptional(); + if (optional != null && !containsManagedOptional(key)) { + if (managedOptionals == null) { + managedOptionals = MMap.emptyNotDone(); } + managedOptionals.put(key, optional); } String localPath = systemDependencyScope == null @@ -394,58 +393,59 @@ public DependencyManagement manageDependency(Dependency dependency) { Key key = new Key(dependency.getArtifact()); if (isApplied()) { - String version = getManagedVersion(key); + AbstractDependencyManager versionOwner = getManagedVersion(key); // is managed locally by model builder // apply only rules coming from "higher" levels - if (version != null) { + if (versionOwner != null) { management = new DependencyManagement(); - management.setVersion(version); + management.setVersion(versionOwner.managedVersions.get(key), versionOwner.path.isEmpty()); } - String scope = getManagedScope(key); + AbstractDependencyManager scopeOwner = getManagedScope(key); // is managed locally by model builder // apply only rules coming from "higher" levels - if (scope != null) { + if (scopeOwner != null) { if (management == null) { management = new DependencyManagement(); } - management.setScope(scope); + String managedScope = scopeOwner.managedScopes.get(key); + management.setScope(managedScope, scopeOwner.path.isEmpty()); if (systemDependencyScope != null - && !systemDependencyScope.is(scope) + && !systemDependencyScope.is(managedScope) && systemDependencyScope.getSystemPath(dependency.getArtifact()) != null) { HashMap properties = new HashMap<>(dependency.getArtifact().getProperties()); systemDependencyScope.setSystemPath(properties, null); - management.setProperties(properties); + management.setProperties(properties, false); } } // system scope paths always applied to have them aligned // (same artifact == same path) in whole graph if (systemDependencyScope != null - && (scope != null && systemDependencyScope.is(scope) - || (scope == null && systemDependencyScope.is(dependency.getScope())))) { - String localPath = getManagedLocalPath(key); - if (localPath != null) { + && (scopeOwner != null && systemDependencyScope.is(scopeOwner.managedScopes.get(key)) + || (scopeOwner == null && systemDependencyScope.is(dependency.getScope())))) { + AbstractDependencyManager localPathOwner = getManagedLocalPath(key); + if (localPathOwner != null) { if (management == null) { management = new DependencyManagement(); } HashMap properties = new HashMap<>(dependency.getArtifact().getProperties()); - systemDependencyScope.setSystemPath(properties, localPath); - management.setProperties(properties); + systemDependencyScope.setSystemPath(properties, localPathOwner.managedLocalPaths.get(key)); + management.setProperties(properties, false); } } // optional is not managed by model builder // apply only rules coming from "higher" levels - Boolean optional = getManagedOptional(key); - if (optional != null) { + AbstractDependencyManager optionalOwner = getManagedOptional(key); + if (optionalOwner != null) { if (management == null) { management = new DependencyManagement(); } - management.setOptional(optional); + management.setOptional(optionalOwner.managedOptionals.get(key), optionalOwner.path.isEmpty()); } } @@ -461,7 +461,7 @@ public DependencyManagement manageDependency(Dependency dependency) { } Collection result = new LinkedHashSet<>(dependency.getExclusions()); result.addAll(exclusions); - management.setExclusions(result); + management.setExclusions(result, false); } return management; @@ -474,34 +474,6 @@ protected boolean isDerived() { return depth < deriveUntil; } - /** - * Manages dependency properties including "version", "scope", "optional", "local path", and "exclusions". - *

- * Property management behavior: - *

    - *
  • Version: Follows {@link #isDerived()} pattern. Management is applied only at higher - * levels to avoid interference with the model builder.
  • - *
  • Scope: Derived from root only due to inheritance in dependency graphs. Special handling - * for "system" scope to align artifact paths.
  • - *
  • Optional: Derived from root only due to inheritance in dependency graphs.
  • - *
  • Local path: Managed only when scope is or was set to "system" to ensure consistent - * artifact path alignment.
  • - *
  • Exclusions: Accumulated additively from root to current level throughout the entire - * dependency path.
  • - *
- *

- * Inheritance handling: Since "scope" and "optional" properties inherit through dependency - * graphs (beyond model builder scope), they are derived only from the root node. The actual manager - * implementation determines specific handling behavior. - *

- * Default behavior: Defaults to {@link #isDerived()} to maintain compatibility with - * "classic" behavior (equivalent to {@code deriveUntil=2}). For custom transitivity management, override - * this method or ensure inherited managed properties are handled during graph transformation. - */ - protected boolean isInheritedDerived() { - return isDerived(); - } - /** * Returns {@code true} if current dependency should be managed according to so far collected/derived rules. */ diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DependencyManagerUtils.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DependencyManagerUtils.java index e83fd0669..685d005f4 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DependencyManagerUtils.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DependencyManagerUtils.java @@ -23,6 +23,7 @@ import org.eclipse.aether.ConfigurationProperties; import org.eclipse.aether.RepositorySystemSession; +import org.eclipse.aether.collection.DependencyManagement; import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.graph.Exclusion; @@ -87,7 +88,7 @@ public final class DependencyManagerUtils { * or if {@link #CONFIG_PROP_VERBOSE} was not enabled */ public static String getPremanagedVersion(DependencyNode node) { - if ((node.getManagedBits() & DependencyNode.MANAGED_VERSION) == 0) { + if (!node.isManagedSubject(DependencyManagement.Subject.VERSION)) { return null; } return cast(node.getData().get(NODE_DATA_PREMANAGED_VERSION), String.class); @@ -101,7 +102,7 @@ public static String getPremanagedVersion(DependencyNode node) { * if {@link #CONFIG_PROP_VERBOSE} was not enabled */ public static String getPremanagedScope(DependencyNode node) { - if ((node.getManagedBits() & DependencyNode.MANAGED_SCOPE) == 0) { + if (!node.isManagedSubject(DependencyManagement.Subject.SCOPE)) { return null; } return cast(node.getData().get(NODE_DATA_PREMANAGED_SCOPE), String.class); @@ -115,7 +116,7 @@ public static String getPremanagedScope(DependencyNode node) { * {@link #CONFIG_PROP_VERBOSE} was not enabled */ public static Boolean getPremanagedOptional(DependencyNode node) { - if ((node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL) == 0) { + if (!node.isManagedSubject(DependencyManagement.Subject.OPTIONAL)) { return null; } return cast(node.getData().get(NODE_DATA_PREMANAGED_OPTIONAL), Boolean.class); @@ -131,7 +132,7 @@ public static Boolean getPremanagedOptional(DependencyNode node) { */ @SuppressWarnings("unchecked") public static Collection getPremanagedExclusions(DependencyNode node) { - if ((node.getManagedBits() & DependencyNode.MANAGED_EXCLUSIONS) == 0) { + if (!node.isManagedSubject(DependencyManagement.Subject.EXCLUSIONS)) { return null; } return cast(node.getData().get(NODE_DATA_PREMANAGED_EXCLUSIONS), Collection.class); @@ -147,7 +148,7 @@ public static Collection getPremanagedExclusions(DependencyNode node) */ @SuppressWarnings("unchecked") public static Map getPremanagedProperties(DependencyNode node) { - if ((node.getManagedBits() & DependencyNode.MANAGED_PROPERTIES) == 0) { + if (!node.isManagedSubject(DependencyManagement.Subject.PROPERTIES)) { return null; } return cast(node.getData().get(NODE_DATA_PREMANAGED_PROPERTIES), Map.class); diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java index b46211be0..460a52c01 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java @@ -145,30 +145,4 @@ protected DependencyManager newInstance( managedExclusions, systemDependencyScope); } - - /** - * Controls inheritance-based property derivation for scope and optional properties. - *

- * Why scope and optional are special: In dependency graphs, these two properties - * are subject to inheritance during graph transformation (which is outside ModelBuilder's scope). - * Therefore, scope and optional are derived only from the root to prevent interference with - * inheritance logic. - *

- *

- * The inheritance problem: If we managed scope/optional from sources below the root, - * we would mark nodes as "managed" in the dependency graph. The "managed" flag means "do not touch it, - * it is as it should be", which would prevent proper inheritance application during later graph - * transformation, causing nodes to end up with incorrect scope or optional states. - *

- *

- * Special case: The "system" scope has special handling due to its unique path requirements. - *

- * - * @return true only at depth 0 (root level) to ensure inheritance-based properties are only - * derived from the root, false otherwise - */ - @Override - protected boolean isInheritedDerived() { - return depth == 0; - } } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/ClassicConflictResolver.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/ClassicConflictResolver.java index 4ecd53b2e..4825e433e 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/ClassicConflictResolver.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/ClassicConflictResolver.java @@ -33,6 +33,7 @@ import org.eclipse.aether.RepositoryException; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.collection.DependencyGraphTransformationContext; +import org.eclipse.aether.collection.DependencyManagement; import org.eclipse.aether.graph.DefaultDependencyNode; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyNode; @@ -675,7 +676,7 @@ private DependencyNode parent() { } private String deriveScope(DependencyNode node, String conflictId) throws RepositoryException { - if ((node.getManagedBits() & DependencyNode.MANAGED_SCOPE) != 0 + if (node.isManagedSubjectEnforced(DependencyManagement.Subject.SCOPE) || (conflictId != null && resolvedIds.containsKey(conflictId))) { return scope(node.getDependency()); } @@ -702,7 +703,7 @@ private boolean deriveOptional(DependencyNode node, String conflictId) { Dependency dep = node.getDependency(); boolean optional = (dep != null) && dep.isOptional(); if (optional - || (node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL) != 0 + || node.isManagedSubjectEnforced(DependencyManagement.Subject.OPTIONAL) || (conflictId != null && resolvedIds.containsKey(conflictId))) { return optional; } diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/PathConflictResolver.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/PathConflictResolver.java index 985106a0b..519e60873 100644 --- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/PathConflictResolver.java +++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/transformer/PathConflictResolver.java @@ -32,6 +32,7 @@ import org.eclipse.aether.RepositorySystemSession; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.collection.DependencyGraphTransformationContext; +import org.eclipse.aether.collection.DependencyManagement; import org.eclipse.aether.graph.DefaultDependencyNode; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyNode; @@ -423,12 +424,12 @@ private void pull(int levels) { private void derive(int levels, boolean winner) throws RepositoryException { if (!winner) { if (this.parent != null) { - if ((dn.getManagedBits() & DependencyNode.MANAGED_SCOPE) == 0) { + if (!dn.isManagedSubjectEnforced(DependencyManagement.Subject.SCOPE)) { ScopeContext context = new ScopeContext(this.parent.scope, this.scope); state.scopeDeriver.deriveScope(context); this.scope = context.derivedScope; } - if ((dn.getManagedBits() & DependencyNode.MANAGED_OPTIONAL) == 0) { + if (!dn.isManagedSubjectEnforced(DependencyManagement.Subject.OPTIONAL)) { if (!this.optional && this.parent.optional) { this.optional = true; }