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