1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.eclipse.aether.util.graph.manager;
20
21 import java.util.Collection;
22
23 import org.eclipse.aether.collection.DependencyManager;
24 import org.eclipse.aether.graph.Exclusion;
25 import org.eclipse.aether.scope.ScopeManager;
26 import org.eclipse.aether.scope.SystemDependencyScope;
27
28 /**
29 * A dependency manager that provides proper transitive dependency management for modern Maven usage.
30 *
31 * <h2>Overview</h2>
32 * <p>
33 * This manager implements proper "transitive dependency management" that works harmoniously
34 * with Maven's ModelBuilder. It produces more precise results regarding versions by respecting
35 * transitive management rules while allowing higher-level management to override lower-level rules.
36 * </p>
37 *
38 * <h2>Key Characteristics</h2>
39 * <ul>
40 * <li><strong>Transitive Management:</strong> {@code deriveUntil=Integer.MAX_VALUE}, {@code applyFrom=2}</li>
41 * <li><strong>ModelBuilder Friendly:</strong> Works in conjunction with, not against, ModelBuilder</li>
42 * <li><strong>Inheritance Aware:</strong> Special handling for scope and optional properties</li>
43 * <li><strong>Precise Versioning:</strong> Obeys transitive management unless managed at higher levels</li>
44 * </ul>
45 *
46 * <h2>Inheritance Handling</h2>
47 * <p>
48 * This manager provides special care for "scope" and "optional" properties that are subject
49 * to inheritance in the dependency graph during later graph transformation steps. These
50 * properties are only derived from the root to prevent interference with inheritance logic.
51 * </p>
52 *
53 * <h2>When to Use</h2>
54 * <p>
55 * This is the <strong>recommended manager for modern Maven projects</strong> that need proper
56 * transitive dependency management while maintaining compatibility with Maven's ModelBuilder.
57 * </p>
58 *
59 * <h2>Comparison with Other Managers</h2>
60 * <ul>
61 * <li>{@link ClassicDependencyManager}: Maven 2.x compatibility, limited transitive support</li>
62 * <li>{@link DefaultDependencyManager}: Aggressive but interferes with ModelBuilder</li>
63 * <li><strong>This manager:</strong> Modern, transitive, ModelBuilder-compatible (recommended)</li>
64 * </ul>
65 *
66 * @author Christian Schulte
67 * @since 1.4.0
68 * @see ClassicDependencyManager
69 * @see DefaultDependencyManager
70 */
71 public final class TransitiveDependencyManager extends AbstractDependencyManager {
72 /**
73 * Creates a new dependency manager without any management information.
74 */
75 public TransitiveDependencyManager() {
76 this(null);
77 }
78
79 /**
80 * Creates a new transitive dependency manager with ModelBuilder-compatible behavior.
81 * <p>
82 * This constructor initializes the manager with settings optimized for modern Maven usage:
83 * <ul>
84 * <li>deriveUntil = Integer.MAX_VALUE (collect management rules at all levels)</li>
85 * <li>applyFrom = 2 (apply management starting from depth 2, respecting ModelBuilder)</li>
86 * <li>Special inheritance handling for scope and optional properties</li>
87 * </ul>
88 *
89 * @param scopeManager application-specific scope manager for handling system dependencies,
90 * may be null to use legacy system dependency scope handling
91 */
92 public TransitiveDependencyManager(ScopeManager scopeManager) {
93 super(Integer.MAX_VALUE, 2, scopeManager);
94 }
95
96 @SuppressWarnings("checkstyle:ParameterNumber")
97 private TransitiveDependencyManager(
98 AbstractDependencyManager parent,
99 int depth,
100 int deriveUntil,
101 int applyFrom,
102 MMap<Key, String> managedVersions,
103 MMap<Key, String> managedScopes,
104 MMap<Key, Boolean> managedOptionals,
105 MMap<Key, String> managedLocalPaths,
106 MMap<Key, Holder<Collection<Exclusion>>> managedExclusions,
107 SystemDependencyScope systemDependencyScope) {
108 super(
109 parent,
110 depth,
111 deriveUntil,
112 applyFrom,
113 managedVersions,
114 managedScopes,
115 managedOptionals,
116 managedLocalPaths,
117 managedExclusions,
118 systemDependencyScope);
119 }
120
121 @Override
122 protected DependencyManager newInstance(
123 MMap<Key, String> managedVersions,
124 MMap<Key, String> managedScopes,
125 MMap<Key, Boolean> managedOptionals,
126 MMap<Key, String> managedLocalPaths,
127 MMap<Key, Holder<Collection<Exclusion>>> managedExclusions) {
128 return new TransitiveDependencyManager(
129 this,
130 depth + 1,
131 deriveUntil,
132 applyFrom,
133 managedVersions,
134 managedScopes,
135 managedOptionals,
136 managedLocalPaths,
137 managedExclusions,
138 systemDependencyScope);
139 }
140
141 /**
142 * Controls inheritance-based property derivation for scope and optional properties.
143 * <p>
144 * <strong>Why scope and optional are special:</strong> In dependency graphs, these two properties
145 * are subject to inheritance during graph transformation (which is outside ModelBuilder's scope).
146 * Therefore, scope and optional are derived only from the root to prevent interference with
147 * inheritance logic.
148 * </p>
149 * <p>
150 * <strong>The inheritance problem:</strong> If we managed scope/optional from sources below the root,
151 * we would mark nodes as "managed" in the dependency graph. The "managed" flag means "do not touch it,
152 * it is as it should be", which would prevent proper inheritance application during later graph
153 * transformation, causing nodes to end up with incorrect scope or optional states.
154 * </p>
155 * <p>
156 * <strong>Special case:</strong> The "system" scope has special handling due to its unique path requirements.
157 * </p>
158 *
159 * @return true only at depth 0 (root level) to ensure inheritance-based properties are only
160 * derived from the root, false otherwise
161 */
162 @Override
163 protected boolean isInheritedDerived() {
164 return depth == 0;
165 }
166 }