001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.util.graph.manager;
020
021import java.util.Collection;
022
023import org.eclipse.aether.collection.DependencyManager;
024import org.eclipse.aether.graph.Exclusion;
025import org.eclipse.aether.scope.ScopeManager;
026import org.eclipse.aether.scope.SystemDependencyScope;
027
028/**
029 * A dependency manager that provides proper transitive dependency management for modern Maven usage.
030 *
031 * <h2>Overview</h2>
032 * <p>
033 * This manager implements proper "transitive dependency management" that works harmoniously
034 * with Maven's ModelBuilder. It produces more precise results regarding versions by respecting
035 * transitive management rules while allowing higher-level management to override lower-level rules.
036 * </p>
037 *
038 * <h2>Key Characteristics</h2>
039 * <ul>
040 * <li><strong>Transitive Management:</strong> {@code deriveUntil=Integer.MAX_VALUE}, {@code applyFrom=2}</li>
041 * <li><strong>ModelBuilder Friendly:</strong> Works in conjunction with, not against, ModelBuilder</li>
042 * <li><strong>Inheritance Aware:</strong> Special handling for scope and optional properties</li>
043 * <li><strong>Precise Versioning:</strong> Obeys transitive management unless managed at higher levels</li>
044 * </ul>
045 *
046 * <h2>Inheritance Handling</h2>
047 * <p>
048 * This manager provides special care for "scope" and "optional" properties that are subject
049 * to inheritance in the dependency graph during later graph transformation steps. These
050 * properties are only derived from the root to prevent interference with inheritance logic.
051 * </p>
052 *
053 * <h2>When to Use</h2>
054 * <p>
055 * This is the <strong>recommended manager for modern Maven projects</strong> that need proper
056 * transitive dependency management while maintaining compatibility with Maven's ModelBuilder.
057 * </p>
058 *
059 * <h2>Comparison with Other Managers</h2>
060 * <ul>
061 * <li>{@link ClassicDependencyManager}: Maven 2.x compatibility, limited transitive support</li>
062 * <li>{@link DefaultDependencyManager}: Aggressive but interferes with ModelBuilder</li>
063 * <li><strong>This manager:</strong> Modern, transitive, ModelBuilder-compatible (recommended)</li>
064 * </ul>
065 *
066 * @author Christian Schulte
067 * @since 1.4.0
068 * @see ClassicDependencyManager
069 * @see DefaultDependencyManager
070 */
071public final class TransitiveDependencyManager extends AbstractDependencyManager {
072    /**
073     * Creates a new dependency manager without any management information.
074     */
075    public TransitiveDependencyManager() {
076        this(null);
077    }
078
079    /**
080     * Creates a new transitive dependency manager with ModelBuilder-compatible behavior.
081     * <p>
082     * This constructor initializes the manager with settings optimized for modern Maven usage:
083     * <ul>
084     * <li>deriveUntil = Integer.MAX_VALUE (collect management rules at all levels)</li>
085     * <li>applyFrom = 2 (apply management starting from depth 2, respecting ModelBuilder)</li>
086     * <li>Special inheritance handling for scope and optional properties</li>
087     * </ul>
088     *
089     * @param scopeManager application-specific scope manager for handling system dependencies,
090     *                     may be null to use legacy system dependency scope handling
091     */
092    public TransitiveDependencyManager(ScopeManager scopeManager) {
093        super(Integer.MAX_VALUE, 2, scopeManager);
094    }
095
096    @SuppressWarnings("checkstyle:ParameterNumber")
097    private TransitiveDependencyManager(
098            AbstractDependencyManager parent,
099            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}