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 transitive dependencies supporting transitive dependency management.
42   *
43   * @author Christian Schulte
44   * @since 1.4.0
45   */
46  public final class TransitiveDependencyManager
47      implements DependencyManager
48  {
49  
50      private final Map<Object, String> managedVersions;
51  
52      private final Map<Object, String> managedScopes;
53  
54      private final Map<Object, Boolean> managedOptionals;
55  
56      private final Map<Object, String> managedLocalPaths;
57  
58      private final Map<Object, Collection<Exclusion>> managedExclusions;
59  
60      private final int depth;
61  
62      private int hashCode;
63  
64      /**
65       * Creates a new dependency manager without any management information.
66       */
67      public TransitiveDependencyManager()
68      {
69          this( 0, Collections.<Object, String>emptyMap(), Collections.<Object, String>emptyMap(),
70                Collections.<Object, Boolean>emptyMap(), Collections.<Object, String>emptyMap(),
71                Collections.<Object, Collection<Exclusion>>emptyMap() );
72      }
73  
74      private TransitiveDependencyManager( final int depth,
75                                           final Map<Object, String> managedVersions,
76                                           final Map<Object, String> managedScopes,
77                                           final Map<Object, Boolean> managedOptionals,
78                                           final Map<Object, String> managedLocalPaths,
79                                           final Map<Object, Collection<Exclusion>> managedExclusions )
80      {
81          super();
82          this.depth = depth;
83          this.managedVersions = managedVersions;
84          this.managedScopes = managedScopes;
85          this.managedOptionals = managedOptionals;
86          this.managedLocalPaths = managedLocalPaths;
87          this.managedExclusions = managedExclusions;
88      }
89  
90      public DependencyManager deriveChildManager( final DependencyCollectionContext context )
91      {
92          requireNonNull( context, "context cannot be null" );
93          Map<Object, String> versions = managedVersions;
94          Map<Object, String> scopes = managedScopes;
95          Map<Object, Boolean> optionals = managedOptionals;
96          Map<Object, String> localPaths = managedLocalPaths;
97          Map<Object, Collection<Exclusion>> exclusions = managedExclusions;
98  
99          for ( Dependency managedDependency : context.getManagedDependencies() )
100         {
101             Artifact artifact = managedDependency.getArtifact();
102             Object key = getKey( artifact );
103 
104             String version = artifact.getVersion();
105             if ( version.length() > 0 && !versions.containsKey( key ) )
106             {
107                 if ( versions == managedVersions )
108                 {
109                     versions = new HashMap<>( managedVersions );
110                 }
111                 versions.put( key, version );
112             }
113 
114             String scope = managedDependency.getScope();
115             if ( scope.length() > 0 && !scopes.containsKey( key ) )
116             {
117                 if ( scopes == this.managedScopes )
118                 {
119                     scopes = new HashMap<>( this.managedScopes );
120                 }
121                 scopes.put( key, scope );
122             }
123 
124             Boolean optional = managedDependency.getOptional();
125             if ( optional != null && !optionals.containsKey( key ) )
126             {
127                 if ( optionals == managedOptionals )
128                 {
129                     optionals = new HashMap<>( managedOptionals );
130                 }
131                 optionals.put( key, optional );
132             }
133 
134             String localPath = managedDependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null );
135             if ( localPath != null && !localPaths.containsKey( key ) )
136             {
137                 if ( localPaths == this.managedLocalPaths )
138                 {
139                     localPaths = new HashMap<>( managedLocalPaths );
140                 }
141                 localPaths.put( key, localPath );
142             }
143 
144             if ( !managedDependency.getExclusions().isEmpty() )
145             {
146                 if ( exclusions == managedExclusions )
147                 {
148                     exclusions = new HashMap<>( managedExclusions );
149                 }
150                 Collection<Exclusion> managed = exclusions.computeIfAbsent( key, k -> new LinkedHashSet<>() );
151                 managed.addAll( managedDependency.getExclusions() );
152             }
153         }
154 
155         return new TransitiveDependencyManager( depth + 1, versions, scopes, optionals, localPaths,
156                                                 exclusions );
157 
158     }
159 
160     public DependencyManagement manageDependency( Dependency dependency )
161     {
162         requireNonNull( dependency, "dependency cannot be null" );
163         DependencyManagement management = null;
164 
165         Object key = getKey( dependency.getArtifact() );
166 
167         if ( depth >= 2 )
168         {
169             String version = managedVersions.get( key );
170             if ( version != null )
171             {
172                 management = new DependencyManagement();
173                 management.setVersion( version );
174             }
175 
176             String scope = managedScopes.get( key );
177             if ( scope != null )
178             {
179                 if ( management == null )
180                 {
181                     management = new DependencyManagement();
182                 }
183                 management.setScope( scope );
184 
185                 if ( !JavaScopes.SYSTEM.equals( scope ) && dependency.getArtifact().getProperty(
186                         ArtifactProperties.LOCAL_PATH, null ) != null )
187                 {
188                     Map<String, String> properties = new HashMap<>( dependency.getArtifact().getProperties() );
189                     properties.remove( ArtifactProperties.LOCAL_PATH );
190                     management.setProperties( properties );
191                 }
192             }
193 
194             if ( ( JavaScopes.SYSTEM.equals( scope ) )
195                      || ( scope == null && JavaScopes.SYSTEM.equals( dependency.getScope() ) ) )
196             {
197                 String localPath = managedLocalPaths.get( key );
198                 if ( localPath != null )
199                 {
200                     if ( management == null )
201                     {
202                         management = new DependencyManagement();
203                     }
204                     Map<String, String> properties = new HashMap<>( dependency.getArtifact().getProperties() );
205                     properties.put( ArtifactProperties.LOCAL_PATH, localPath );
206                     management.setProperties( properties );
207                 }
208             }
209 
210             Boolean optional = managedOptionals.get( key );
211             if ( optional != null )
212             {
213                 if ( management == null )
214                 {
215                     management = new DependencyManagement();
216                 }
217                 management.setOptional( optional );
218             }
219         }
220 
221         Collection<Exclusion> exclusions = managedExclusions.get( key );
222         if ( exclusions != null )
223         {
224             if ( management == null )
225             {
226                 management = new DependencyManagement();
227             }
228             Collection<Exclusion> result = new LinkedHashSet<>( dependency.getExclusions() );
229             result.addAll( exclusions );
230             management.setExclusions( result );
231         }
232 
233         return management;
234     }
235 
236     private Object getKey( Artifact a )
237     {
238         return new Key( a );
239     }
240 
241     @Override
242     public boolean equals( final Object obj )
243     {
244         boolean equal = obj instanceof TransitiveDependencyManager;
245 
246         if ( equal )
247         {
248             final TransitiveDependencyManager that = (TransitiveDependencyManager) obj;
249             return depth == that.depth
250                        && 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         return false;
257     }
258 
259     @Override
260     public int hashCode()
261     {
262         if ( hashCode == 0 )
263         {
264             hashCode = Objects.hash( depth, managedVersions, managedScopes, managedOptionals, managedExclusions );
265         }
266         return hashCode;
267     }
268 
269     static class Key
270     {
271         private final Artifact artifact;
272 
273         private final int hashCode;
274 
275         Key( final Artifact artifact )
276         {
277             this.artifact = artifact;
278             this.hashCode = Objects.hash( artifact.getGroupId(), artifact.getArtifactId() );
279         }
280 
281         @Override
282         public boolean equals( final Object obj )
283         {
284             boolean equal = obj instanceof Key;
285 
286             if ( equal )
287             {
288                 final Key that = (Key) obj;
289                 return Objects.equals( artifact.getArtifactId(), that.artifact.getArtifactId() )
290                            && Objects.equals( artifact.getGroupId(), that.artifact.getGroupId() )
291                            && Objects.equals( artifact.getExtension(), that.artifact.getExtension() )
292                            && Objects.equals( artifact.getClassifier(), that.artifact.getClassifier() );
293             }
294 
295             return false;
296         }
297 
298         @Override
299         public int hashCode()
300         {
301             return this.hashCode;
302         }
303 
304     }
305 
306 }