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;
022import java.util.Collections;
023import java.util.HashMap;
024import java.util.LinkedHashSet;
025import java.util.Map;
026import java.util.Objects;
027
028import org.eclipse.aether.artifact.Artifact;
029import org.eclipse.aether.artifact.ArtifactProperties;
030import org.eclipse.aether.collection.DependencyCollectionContext;
031import org.eclipse.aether.collection.DependencyManagement;
032import org.eclipse.aether.collection.DependencyManager;
033import org.eclipse.aether.graph.Dependency;
034import org.eclipse.aether.graph.Exclusion;
035import org.eclipse.aether.util.artifact.DependencyScopes;
036
037import static java.util.Objects.requireNonNull;
038
039/**
040 * A dependency manager support class.
041 *
042 * @since 2.0.0
043 */
044public abstract class AbstractDependencyManager implements DependencyManager {
045
046    protected final int depth;
047
048    protected final int deriveUntil;
049
050    protected final int applyFrom;
051
052    protected final Map<Object, String> managedVersions;
053
054    protected final Map<Object, String> managedScopes;
055
056    protected final Map<Object, Boolean> managedOptionals;
057
058    protected final Map<Object, String> managedLocalPaths;
059
060    protected final Map<Object, Collection<Exclusion>> managedExclusions;
061
062    private final int hashCode;
063
064    /**
065     * Creates a new dependency manager without any management information.
066     */
067    protected AbstractDependencyManager(int deriveUntil, int applyFrom) {
068        this(
069                0,
070                deriveUntil,
071                applyFrom,
072                Collections.emptyMap(),
073                Collections.emptyMap(),
074                Collections.emptyMap(),
075                Collections.emptyMap(),
076                Collections.emptyMap());
077    }
078
079    @SuppressWarnings("checkstyle:ParameterNumber")
080    protected AbstractDependencyManager(
081            int depth,
082            int deriveUntil,
083            int applyFrom,
084            Map<Object, String> managedVersions,
085            Map<Object, String> managedScopes,
086            Map<Object, Boolean> managedOptionals,
087            Map<Object, String> managedLocalPaths,
088            Map<Object, Collection<Exclusion>> managedExclusions) {
089        this.depth = depth;
090        this.deriveUntil = deriveUntil;
091        this.applyFrom = applyFrom;
092        this.managedVersions = managedVersions;
093        this.managedScopes = managedScopes;
094        this.managedOptionals = managedOptionals;
095        this.managedLocalPaths = managedLocalPaths;
096        this.managedExclusions = managedExclusions;
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 = managedDependency.getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null);
158            if (localPath != null && !managedLocalPaths.containsKey(key)) {
159                if (managedLocalPaths == this.managedLocalPaths) {
160                    managedLocalPaths = new HashMap<>(this.managedLocalPaths);
161                }
162                managedLocalPaths.put(key, localPath);
163            }
164
165            Collection<Exclusion> exclusions = managedDependency.getExclusions();
166            if (!exclusions.isEmpty()) {
167                if (managedExclusions == this.managedExclusions) {
168                    managedExclusions = new HashMap<>(this.managedExclusions);
169                }
170                Collection<Exclusion> managed = managedExclusions.computeIfAbsent(key, k -> new LinkedHashSet<>());
171                managed.addAll(exclusions);
172            }
173        }
174
175        return newInstance(managedVersions, managedScopes, managedOptionals, managedLocalPaths, managedExclusions);
176    }
177
178    @Override
179    public DependencyManagement manageDependency(Dependency dependency) {
180        requireNonNull(dependency, "dependency cannot be null");
181        DependencyManagement management = null;
182        Object key = new Key(dependency.getArtifact());
183
184        if (depth >= applyFrom) {
185            String version = managedVersions.get(key);
186            if (version != null) {
187                management = new DependencyManagement();
188                management.setVersion(version);
189            }
190
191            String scope = managedScopes.get(key);
192            if (scope != null) {
193                if (management == null) {
194                    management = new DependencyManagement();
195                }
196                management.setScope(scope);
197
198                if (!DependencyScopes.SYSTEM.equals(scope)
199                        && dependency.getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null) != null) {
200                    Map<String, String> properties =
201                            new HashMap<>(dependency.getArtifact().getProperties());
202                    properties.remove(ArtifactProperties.LOCAL_PATH);
203                    management.setProperties(properties);
204                }
205            }
206
207            if ((DependencyScopes.SYSTEM.equals(scope))
208                    || (scope == null && DependencyScopes.SYSTEM.equals(dependency.getScope()))) {
209                String localPath = managedLocalPaths.get(key);
210                if (localPath != null) {
211                    if (management == null) {
212                        management = new DependencyManagement();
213                    }
214                    Map<String, String> properties =
215                            new HashMap<>(dependency.getArtifact().getProperties());
216                    properties.put(ArtifactProperties.LOCAL_PATH, localPath);
217                    management.setProperties(properties);
218                }
219            }
220
221            Boolean optional = managedOptionals.get(key);
222            if (optional != null) {
223                if (management == null) {
224                    management = new DependencyManagement();
225                }
226                management.setOptional(optional);
227            }
228        }
229
230        Collection<Exclusion> exclusions = managedExclusions.get(key);
231        if (exclusions != null) {
232            if (management == null) {
233                management = new DependencyManagement();
234            }
235            Collection<Exclusion> result = new LinkedHashSet<>(dependency.getExclusions());
236            result.addAll(exclusions);
237            management.setExclusions(result);
238        }
239
240        return management;
241    }
242
243    @Override
244    public boolean equals(Object obj) {
245        if (this == obj) {
246            return true;
247        } else if (null == obj || !getClass().equals(obj.getClass())) {
248            return false;
249        }
250
251        AbstractDependencyManager that = (AbstractDependencyManager) obj;
252        return depth == that.depth
253                && deriveUntil == that.deriveUntil
254                && applyFrom == that.applyFrom
255                && managedVersions.equals(that.managedVersions)
256                && managedScopes.equals(that.managedScopes)
257                && managedOptionals.equals(that.managedOptionals)
258                && managedExclusions.equals(that.managedExclusions);
259    }
260
261    @Override
262    public int hashCode() {
263        return hashCode;
264    }
265
266    protected static class Key {
267
268        private final Artifact artifact;
269
270        private final int hashCode;
271
272        Key(Artifact artifact) {
273            this.artifact = artifact;
274            this.hashCode = Objects.hash(artifact.getGroupId(), artifact.getArtifactId());
275        }
276
277        @Override
278        public boolean equals(Object obj) {
279            if (obj == this) {
280                return true;
281            } else if (!(obj instanceof Key)) {
282                return false;
283            }
284            Key that = (Key) obj;
285            return artifact.getArtifactId().equals(that.artifact.getArtifactId())
286                    && artifact.getGroupId().equals(that.artifact.getGroupId())
287                    && artifact.getExtension().equals(that.artifact.getExtension())
288                    && artifact.getClassifier().equals(that.artifact.getClassifier());
289        }
290
291        @Override
292        public int hashCode() {
293            return hashCode;
294        }
295    }
296}