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.*;
022
023import org.eclipse.aether.SystemScopeHandler;
024import org.eclipse.aether.artifact.Artifact;
025import org.eclipse.aether.collection.DependencyCollectionContext;
026import org.eclipse.aether.collection.DependencyManagement;
027import org.eclipse.aether.collection.DependencyManager;
028import org.eclipse.aether.graph.Dependency;
029import org.eclipse.aether.graph.Exclusion;
030
031import static java.util.Objects.requireNonNull;
032
033/**
034 * A dependency manager support class.
035 *
036 * @since 2.0.0
037 */
038public abstract class AbstractDependencyManager implements DependencyManager {
039    /**
040     * This predicate is here ONLY to support deprecated constructors.
041     *
042     * @deprecated To be removed when deprecated constructors are removed.
043     */
044    @Deprecated
045    protected static final SystemScopeHandler SYSTEM_SCOPE_HANDLER = SystemScopeHandler.LEGACY;
046
047    protected final int depth;
048
049    protected final int deriveUntil;
050
051    protected final int applyFrom;
052
053    protected final Map<Object, String> managedVersions;
054
055    protected final Map<Object, String> managedScopes;
056
057    protected final Map<Object, Boolean> managedOptionals;
058
059    protected final Map<Object, String> managedLocalPaths;
060
061    protected final Map<Object, Collection<Exclusion>> managedExclusions;
062
063    protected final SystemScopeHandler systemScopeHandler;
064
065    private final int hashCode;
066
067    protected AbstractDependencyManager(int deriveUntil, int applyFrom, SystemScopeHandler systemScopeHandler) {
068        this(
069                0,
070                deriveUntil,
071                applyFrom,
072                Collections.emptyMap(),
073                Collections.emptyMap(),
074                Collections.emptyMap(),
075                Collections.emptyMap(),
076                Collections.emptyMap(),
077                systemScopeHandler);
078    }
079
080    @SuppressWarnings("checkstyle:ParameterNumber")
081    protected AbstractDependencyManager(
082            int depth,
083            int deriveUntil,
084            int applyFrom,
085            Map<Object, String> managedVersions,
086            Map<Object, String> managedScopes,
087            Map<Object, Boolean> managedOptionals,
088            Map<Object, String> managedLocalPaths,
089            Map<Object, Collection<Exclusion>> managedExclusions,
090            SystemScopeHandler systemScopeHandler) {
091        this.depth = depth;
092        this.deriveUntil = deriveUntil;
093        this.applyFrom = applyFrom;
094        this.managedVersions = requireNonNull(managedVersions);
095        this.managedScopes = requireNonNull(managedScopes);
096        this.managedOptionals = requireNonNull(managedOptionals);
097        this.managedLocalPaths = requireNonNull(managedLocalPaths);
098        this.managedExclusions = requireNonNull(managedExclusions);
099        this.systemScopeHandler = requireNonNull(systemScopeHandler);
100
101        this.hashCode = Objects.hash(
102                depth,
103                deriveUntil,
104                applyFrom,
105                managedVersions,
106                managedScopes,
107                managedOptionals,
108                managedLocalPaths,
109                managedExclusions);
110    }
111
112    protected abstract DependencyManager newInstance(
113            Map<Object, String> managedVersions,
114            Map<Object, String> managedScopes,
115            Map<Object, Boolean> managedOptionals,
116            Map<Object, String> managedLocalPaths,
117            Map<Object, Collection<Exclusion>> managedExclusions);
118
119    @Override
120    public DependencyManager deriveChildManager(DependencyCollectionContext context) {
121        requireNonNull(context, "context cannot be null");
122        if (depth >= deriveUntil) {
123            return this;
124        }
125
126        Map<Object, String> managedVersions = this.managedVersions;
127        Map<Object, String> managedScopes = this.managedScopes;
128        Map<Object, Boolean> managedOptionals = this.managedOptionals;
129        Map<Object, String> managedLocalPaths = this.managedLocalPaths;
130        Map<Object, Collection<Exclusion>> managedExclusions = this.managedExclusions;
131
132        for (Dependency managedDependency : context.getManagedDependencies()) {
133            Artifact artifact = managedDependency.getArtifact();
134            Object key = new Key(artifact);
135
136            String version = artifact.getVersion();
137            if (!version.isEmpty() && !managedVersions.containsKey(key)) {
138                if (managedVersions == this.managedVersions) {
139                    managedVersions = new HashMap<>(this.managedVersions);
140                }
141                managedVersions.put(key, version);
142            }
143
144            String scope = managedDependency.getScope();
145            if (!scope.isEmpty() && !managedScopes.containsKey(key)) {
146                if (managedScopes == this.managedScopes) {
147                    managedScopes = new HashMap<>(this.managedScopes);
148                }
149                managedScopes.put(key, scope);
150            }
151
152            Boolean optional = managedDependency.getOptional();
153            if (optional != null && !managedOptionals.containsKey(key)) {
154                if (managedOptionals == this.managedOptionals) {
155                    managedOptionals = new HashMap<>(this.managedOptionals);
156                }
157                managedOptionals.put(key, optional);
158            }
159
160            String localPath = systemScopeHandler.getSystemPath(managedDependency.getArtifact());
161            if (localPath != null && !managedLocalPaths.containsKey(key)) {
162                if (managedLocalPaths == this.managedLocalPaths) {
163                    managedLocalPaths = new HashMap<>(this.managedLocalPaths);
164                }
165                managedLocalPaths.put(key, localPath);
166            }
167
168            Collection<Exclusion> exclusions = managedDependency.getExclusions();
169            if (!exclusions.isEmpty()) {
170                if (managedExclusions == this.managedExclusions) {
171                    managedExclusions = new HashMap<>(this.managedExclusions);
172                }
173                Collection<Exclusion> managed = managedExclusions.computeIfAbsent(key, k -> new LinkedHashSet<>());
174                managed.addAll(exclusions);
175            }
176        }
177
178        return newInstance(managedVersions, managedScopes, managedOptionals, managedLocalPaths, managedExclusions);
179    }
180
181    @Override
182    public DependencyManagement manageDependency(Dependency dependency) {
183        requireNonNull(dependency, "dependency cannot be null");
184        DependencyManagement management = null;
185        Object key = new Key(dependency.getArtifact());
186
187        if (depth >= applyFrom) {
188            String version = managedVersions.get(key);
189            if (version != null) {
190                management = new DependencyManagement();
191                management.setVersion(version);
192            }
193
194            String scope = managedScopes.get(key);
195            if (scope != null) {
196                if (management == null) {
197                    management = new DependencyManagement();
198                }
199                management.setScope(scope);
200
201                if (!systemScopeHandler.isSystemScope(scope)
202                        && systemScopeHandler.getSystemPath(dependency.getArtifact()) != null) {
203                    Map<String, String> properties =
204                            new HashMap<>(dependency.getArtifact().getProperties());
205                    systemScopeHandler.setSystemPath(properties, null);
206                    management.setProperties(properties);
207                }
208            }
209
210            if ((systemScopeHandler.isSystemScope(scope))
211                    || (scope == null && systemScopeHandler.isSystemScope(dependency.getScope()))) {
212                String localPath = managedLocalPaths.get(key);
213                if (localPath != null) {
214                    if (management == null) {
215                        management = new DependencyManagement();
216                    }
217                    Map<String, String> properties =
218                            new HashMap<>(dependency.getArtifact().getProperties());
219                    systemScopeHandler.setSystemPath(properties, localPath);
220                    management.setProperties(properties);
221                }
222            }
223
224            Boolean optional = managedOptionals.get(key);
225            if (optional != null) {
226                if (management == null) {
227                    management = new DependencyManagement();
228                }
229                management.setOptional(optional);
230            }
231        }
232
233        Collection<Exclusion> exclusions = managedExclusions.get(key);
234        if (exclusions != null) {
235            if (management == null) {
236                management = new DependencyManagement();
237            }
238            Collection<Exclusion> result = new LinkedHashSet<>(dependency.getExclusions());
239            result.addAll(exclusions);
240            management.setExclusions(result);
241        }
242
243        return management;
244    }
245
246    @Override
247    public boolean equals(Object obj) {
248        if (this == obj) {
249            return true;
250        } else if (null == obj || !getClass().equals(obj.getClass())) {
251            return false;
252        }
253
254        AbstractDependencyManager that = (AbstractDependencyManager) obj;
255        return depth == that.depth
256                && deriveUntil == that.deriveUntil
257                && applyFrom == that.applyFrom
258                && managedVersions.equals(that.managedVersions)
259                && managedScopes.equals(that.managedScopes)
260                && managedOptionals.equals(that.managedOptionals)
261                && managedExclusions.equals(that.managedExclusions);
262    }
263
264    @Override
265    public int hashCode() {
266        return hashCode;
267    }
268
269    protected static class Key {
270
271        private final Artifact artifact;
272
273        private final int hashCode;
274
275        Key(Artifact artifact) {
276            this.artifact = artifact;
277            this.hashCode = Objects.hash(artifact.getGroupId(), artifact.getArtifactId());
278        }
279
280        @Override
281        public boolean equals(Object obj) {
282            if (obj == this) {
283                return true;
284            } else if (!(obj instanceof Key)) {
285                return false;
286            }
287            Key that = (Key) obj;
288            return artifact.getArtifactId().equals(that.artifact.getArtifactId())
289                    && artifact.getGroupId().equals(that.artifact.getGroupId())
290                    && artifact.getExtension().equals(that.artifact.getExtension())
291                    && artifact.getClassifier().equals(that.artifact.getClassifier());
292        }
293
294        @Override
295        public int hashCode() {
296            return hashCode;
297        }
298
299        @Override
300        public String toString() {
301            return String.valueOf(artifact);
302        }
303    }
304}