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.internal.impl.scope;
020
021import org.eclipse.aether.impl.scope.InternalScopeManager;
022import org.eclipse.aether.scope.DependencyScope;
023import org.eclipse.aether.scope.ScopeManager;
024import org.eclipse.aether.util.graph.transformer.ConflictResolver;
025import org.eclipse.aether.util.graph.transformer.ConflictResolver.ScopeContext;
026import org.eclipse.aether.util.graph.transformer.ConflictResolver.ScopeDeriver;
027
028import static java.util.Objects.requireNonNull;
029
030/**
031 * A scope deriver for use with {@link ConflictResolver} that supports the scopes from {@link ScopeManager}. It basically
032 * chooses "narrowest" scope, based on parent and child scopes.
033 * <p>
034 * This class also "bridges" between {@link DependencyScope} and Resolver that uses plain string labels for scopes.
035 *
036 * @since 4.0.0
037 */
038public final class ManagedScopeDeriver extends ScopeDeriver {
039    private final InternalScopeManager scopeManager;
040    private final DependencyScope systemScope;
041
042    public ManagedScopeDeriver(InternalScopeManager scopeManager) {
043        this.scopeManager = requireNonNull(scopeManager, "scopeManager");
044        this.systemScope = scopeManager.getSystemDependencyScope().orElse(null);
045    }
046
047    @Override
048    public void deriveScope(ScopeContext context) {
049        context.setDerivedScope(getDerivedScope(context.getParentScope(), context.getChildScope()));
050    }
051
052    /**
053     * Visible for testing. It chooses "narrowest" scope out of parent or child, unless child is system scope.
054     */
055    public String getDerivedScope(String parentScope, String childScope) {
056        // ask parent scope (nullable)
057        DependencyScope parent = parentScope != null
058                ? scopeManager.getDependencyScope(parentScope).orElse(null)
059                : null;
060        // ask child scope (non-nullable, but may be unknown scope to manager)
061        DependencyScope child = scopeManager.getDependencyScope(childScope).orElse(null);
062
063        // if system scope exists and child is system scope: system
064        if (systemScope != null && systemScope == child) {
065            return systemScope.getId();
066        }
067        // if no parent (i.e. is root): child scope as-is
068        if (parent == null) {
069            return child != null ? child.getId() : "";
070        }
071        if (child == null) {
072            return parent.getId();
073        }
074        // otherwise the narrowest out of parent or child
075        int parentWidth = scopeManager.getDependencyScopeWidth(parent);
076        int childWidth = scopeManager.getDependencyScopeWidth(child);
077        if (parentWidth < childWidth) {
078            return parent.getId();
079        } else {
080            return child.getId();
081        }
082    }
083}