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