View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.util.graph.manager;
20  
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.HashMap;
24  import java.util.LinkedHashSet;
25  import java.util.Map;
26  
27  import org.eclipse.aether.artifact.Artifact;
28  import org.eclipse.aether.artifact.ArtifactProperties;
29  import org.eclipse.aether.collection.DependencyCollectionContext;
30  import org.eclipse.aether.collection.DependencyManagement;
31  import org.eclipse.aether.collection.DependencyManager;
32  import org.eclipse.aether.graph.Dependency;
33  import org.eclipse.aether.graph.Exclusion;
34  import org.eclipse.aether.util.artifact.JavaScopes;
35  
36  import static java.util.Objects.requireNonNull;
37  
38  /**
39   * A dependency manager that mimics the way Maven 2.x works.
40   */
41  public final class ClassicDependencyManager implements DependencyManager {
42  
43      private final int depth;
44  
45      private final Map<Object, String> managedVersions;
46  
47      private final Map<Object, String> managedScopes;
48  
49      private final Map<Object, Boolean> managedOptionals;
50  
51      private final Map<Object, String> managedLocalPaths;
52  
53      private final Map<Object, Collection<Exclusion>> managedExclusions;
54  
55      private int hashCode;
56  
57      /**
58       * Creates a new dependency manager without any management information.
59       */
60      public ClassicDependencyManager() {
61          this(
62                  0,
63                  Collections.<Object, String>emptyMap(),
64                  Collections.<Object, String>emptyMap(),
65                  Collections.<Object, Boolean>emptyMap(),
66                  Collections.<Object, String>emptyMap(),
67                  Collections.<Object, Collection<Exclusion>>emptyMap());
68      }
69  
70      private ClassicDependencyManager(
71              int depth,
72              Map<Object, String> managedVersions,
73              Map<Object, String> managedScopes,
74              Map<Object, Boolean> managedOptionals,
75              Map<Object, String> managedLocalPaths,
76              Map<Object, Collection<Exclusion>> managedExclusions) {
77          this.depth = depth;
78          this.managedVersions = managedVersions;
79          this.managedScopes = managedScopes;
80          this.managedOptionals = managedOptionals;
81          this.managedLocalPaths = managedLocalPaths;
82          this.managedExclusions = managedExclusions;
83      }
84  
85      public DependencyManager deriveChildManager(DependencyCollectionContext context) {
86          requireNonNull(context, "context cannot be null");
87          if (depth >= 2) {
88              return this;
89          } else if (depth == 1) {
90              return new ClassicDependencyManager(
91                      depth + 1, managedVersions, managedScopes, managedOptionals, managedLocalPaths, managedExclusions);
92          }
93  
94          Map<Object, String> managedVersions = this.managedVersions;
95          Map<Object, String> managedScopes = this.managedScopes;
96          Map<Object, Boolean> managedOptionals = this.managedOptionals;
97          Map<Object, String> managedLocalPaths = this.managedLocalPaths;
98          Map<Object, Collection<Exclusion>> managedExclusions = this.managedExclusions;
99  
100         for (Dependency managedDependency : context.getManagedDependencies()) {
101             Artifact artifact = managedDependency.getArtifact();
102             Object key = getKey(artifact);
103 
104             String version = artifact.getVersion();
105             if (version.length() > 0 && !managedVersions.containsKey(key)) {
106                 if (managedVersions == this.managedVersions) {
107                     managedVersions = new HashMap<>(this.managedVersions);
108                 }
109                 managedVersions.put(key, version);
110             }
111 
112             String scope = managedDependency.getScope();
113             if (scope.length() > 0 && !managedScopes.containsKey(key)) {
114                 if (managedScopes == this.managedScopes) {
115                     managedScopes = new HashMap<>(this.managedScopes);
116                 }
117                 managedScopes.put(key, scope);
118             }
119 
120             Boolean optional = managedDependency.getOptional();
121             if (optional != null && !managedOptionals.containsKey(key)) {
122                 if (managedOptionals == this.managedOptionals) {
123                     managedOptionals = new HashMap<>(this.managedOptionals);
124                 }
125                 managedOptionals.put(key, optional);
126             }
127 
128             String localPath = managedDependency.getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null);
129             if (localPath != null && !managedLocalPaths.containsKey(key)) {
130                 if (managedLocalPaths == this.managedLocalPaths) {
131                     managedLocalPaths = new HashMap<>(this.managedLocalPaths);
132                 }
133                 managedLocalPaths.put(key, localPath);
134             }
135 
136             Collection<Exclusion> exclusions = managedDependency.getExclusions();
137             if (!exclusions.isEmpty()) {
138                 if (managedExclusions == this.managedExclusions) {
139                     managedExclusions = new HashMap<>(this.managedExclusions);
140                 }
141                 Collection<Exclusion> managed = managedExclusions.computeIfAbsent(key, k -> new LinkedHashSet<>());
142                 managed.addAll(exclusions);
143             }
144         }
145 
146         return new ClassicDependencyManager(
147                 depth + 1, managedVersions, managedScopes, managedOptionals, managedLocalPaths, managedExclusions);
148     }
149 
150     public DependencyManagement manageDependency(Dependency dependency) {
151         requireNonNull(dependency, "dependency cannot be null");
152         DependencyManagement management = null;
153 
154         Object key = getKey(dependency.getArtifact());
155 
156         if (depth >= 2) {
157             String version = managedVersions.get(key);
158             if (version != null) {
159                 management = new DependencyManagement();
160                 management.setVersion(version);
161             }
162 
163             String scope = managedScopes.get(key);
164             if (scope != null) {
165                 if (management == null) {
166                     management = new DependencyManagement();
167                 }
168                 management.setScope(scope);
169 
170                 if (!JavaScopes.SYSTEM.equals(scope)
171                         && dependency.getArtifact().getProperty(ArtifactProperties.LOCAL_PATH, null) != null) {
172                     Map<String, String> properties =
173                             new HashMap<>(dependency.getArtifact().getProperties());
174                     properties.remove(ArtifactProperties.LOCAL_PATH);
175                     management.setProperties(properties);
176                 }
177             }
178 
179             if ((JavaScopes.SYSTEM.equals(scope))
180                     || (scope == null && JavaScopes.SYSTEM.equals(dependency.getScope()))) {
181                 String localPath = managedLocalPaths.get(key);
182                 if (localPath != null) {
183                     if (management == null) {
184                         management = new DependencyManagement();
185                     }
186                     Map<String, String> properties =
187                             new HashMap<>(dependency.getArtifact().getProperties());
188                     properties.put(ArtifactProperties.LOCAL_PATH, localPath);
189                     management.setProperties(properties);
190                 }
191             }
192 
193             Boolean optional = managedOptionals.get(key);
194             if (optional != null) {
195                 if (management == null) {
196                     management = new DependencyManagement();
197                 }
198                 management.setOptional(optional);
199             }
200         }
201 
202         Collection<Exclusion> exclusions = managedExclusions.get(key);
203         if (exclusions != null) {
204             if (management == null) {
205                 management = new DependencyManagement();
206             }
207             Collection<Exclusion> result = new LinkedHashSet<>(dependency.getExclusions());
208             result.addAll(exclusions);
209             management.setExclusions(result);
210         }
211 
212         return management;
213     }
214 
215     private Object getKey(Artifact a) {
216         return new Key(a);
217     }
218 
219     @Override
220     public boolean equals(Object obj) {
221         if (this == obj) {
222             return true;
223         } else if (null == obj || !getClass().equals(obj.getClass())) {
224             return false;
225         }
226 
227         ClassicDependencyManager that = (ClassicDependencyManager) obj;
228         return depth == that.depth
229                 && managedVersions.equals(that.managedVersions)
230                 && managedScopes.equals(that.managedScopes)
231                 && managedOptionals.equals(that.managedOptionals)
232                 && managedExclusions.equals(that.managedExclusions);
233     }
234 
235     @Override
236     public int hashCode() {
237         if (hashCode == 0) {
238             int hash = 17;
239             hash = hash * 31 + depth;
240             hash = hash * 31 + managedVersions.hashCode();
241             hash = hash * 31 + managedScopes.hashCode();
242             hash = hash * 31 + managedOptionals.hashCode();
243             hash = hash * 31 + managedExclusions.hashCode();
244             hashCode = hash;
245         }
246         return hashCode;
247     }
248 
249     static class Key {
250 
251         private final Artifact artifact;
252 
253         private final int hashCode;
254 
255         Key(Artifact artifact) {
256             this.artifact = artifact;
257 
258             int hash = 17;
259             hash = hash * 31 + artifact.getGroupId().hashCode();
260             hash = hash * 31 + artifact.getArtifactId().hashCode();
261             hashCode = hash;
262         }
263 
264         @Override
265         public boolean equals(Object obj) {
266             if (obj == this) {
267                 return true;
268             } else if (!(obj instanceof Key)) {
269                 return false;
270             }
271             Key that = (Key) obj;
272             return artifact.getArtifactId().equals(that.artifact.getArtifactId())
273                     && artifact.getGroupId().equals(that.artifact.getGroupId())
274                     && artifact.getExtension().equals(that.artifact.getExtension())
275                     && artifact.getClassifier().equals(that.artifact.getClassifier());
276         }
277 
278         @Override
279         public int hashCode() {
280             return hashCode;
281         }
282     }
283 }