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