001package org.eclipse.aether.util.graph.manager;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.util.Collection;
023import java.util.Collections;
024import java.util.HashMap;
025import java.util.LinkedHashSet;
026import java.util.Map;
027import java.util.Objects;
028
029import org.eclipse.aether.artifact.Artifact;
030import org.eclipse.aether.artifact.ArtifactProperties;
031import org.eclipse.aether.collection.DependencyCollectionContext;
032import org.eclipse.aether.collection.DependencyManagement;
033import org.eclipse.aether.collection.DependencyManager;
034import org.eclipse.aether.graph.Dependency;
035import org.eclipse.aether.graph.Exclusion;
036import org.eclipse.aether.util.artifact.JavaScopes;
037
038/**
039 * A dependency manager managing dependencies on all levels supporting transitive dependency management.
040 * <p>
041 * <b>Note:</b>Unlike the {@code ClassicDependencyManager} and the {@code TransitiveDependencyManager} this
042 * implementation applies management also on the first level. This is considered the resolver's default behaviour.
043 * It ignores all management overrides supported by the {@code MavenModelBuilder}.
044 * </p>
045 *
046 * @author Christian Schulte
047 * @since 1.4.0
048 */
049public final class DefaultDependencyManager
050    implements DependencyManager
051{
052
053    private final Map<Object, String> managedVersions;
054
055    private final Map<Object, String> managedScopes;
056
057    private final Map<Object, Boolean> managedOptionals;
058
059    private final Map<Object, String> managedLocalPaths;
060
061    private final Map<Object, Collection<Exclusion>> managedExclusions;
062
063    private int hashCode;
064
065    /**
066     * Creates a new dependency manager without any management information.
067     */
068    public DefaultDependencyManager()
069    {
070        this( Collections.<Object, String>emptyMap(), Collections.<Object, String>emptyMap(),
071              Collections.<Object, Boolean>emptyMap(), Collections.<Object, String>emptyMap(),
072              Collections.<Object, Collection<Exclusion>>emptyMap() );
073    }
074
075    private DefaultDependencyManager( final Map<Object, String> managedVersions,
076                                      final Map<Object, String> managedScopes,
077                                      final Map<Object, Boolean> managedOptionals,
078                                      final Map<Object, String> managedLocalPaths,
079                                      final Map<Object, Collection<Exclusion>> managedExclusions )
080    {
081        super();
082        this.managedVersions = managedVersions;
083        this.managedScopes = managedScopes;
084        this.managedOptionals = managedOptionals;
085        this.managedLocalPaths = managedLocalPaths;
086        this.managedExclusions = managedExclusions;
087    }
088
089    public DependencyManager deriveChildManager( final DependencyCollectionContext context )
090    {
091        Map<Object, String> versions = this.managedVersions;
092        Map<Object, String> scopes = this.managedScopes;
093        Map<Object, Boolean> optionals = this.managedOptionals;
094        Map<Object, String> localPaths = this.managedLocalPaths;
095        Map<Object, Collection<Exclusion>> exclusions = this.managedExclusions;
096
097        for ( Dependency managedDependency : context.getManagedDependencies() )
098        {
099            Artifact artifact = managedDependency.getArtifact();
100            Object key = getKey( artifact );
101
102            String version = artifact.getVersion();
103            if ( version.length() > 0 && !versions.containsKey( key ) )
104            {
105                if ( versions == this.managedVersions )
106                {
107                    versions = new HashMap<>( this.managedVersions );
108                }
109                versions.put( key, version );
110            }
111
112            String scope = managedDependency.getScope();
113            if ( scope.length() > 0 && !scopes.containsKey( key ) )
114            {
115                if ( scopes == this.managedScopes )
116                {
117                    scopes = new HashMap<>( this.managedScopes );
118                }
119                scopes.put( key, scope );
120            }
121
122            Boolean optional = managedDependency.getOptional();
123            if ( optional != null && !optionals.containsKey( key ) )
124            {
125                if ( optionals == this.managedOptionals )
126                {
127                    optionals = new HashMap<>( this.managedOptionals );
128                }
129                optionals.put( key, optional );
130            }
131
132            String localPath = managedDependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null );
133            if ( localPath != null && !localPaths.containsKey( key ) )
134            {
135                if ( localPaths == this.managedLocalPaths )
136                {
137                    localPaths = new HashMap<>( this.managedLocalPaths );
138                }
139                localPaths.put( key, localPath );
140            }
141
142            if ( !managedDependency.getExclusions().isEmpty() )
143            {
144                if ( exclusions == this.managedExclusions )
145                {
146                    exclusions = new HashMap<>( this.managedExclusions );
147                }
148                Collection<Exclusion> managed = exclusions.get( key );
149                if ( managed == null )
150                {
151                    managed = new LinkedHashSet<>();
152                    exclusions.put( key, managed );
153                }
154                managed.addAll( managedDependency.getExclusions() );
155            }
156        }
157
158        return new DefaultDependencyManager( versions, scopes, optionals, localPaths, exclusions );
159    }
160
161    public DependencyManagement manageDependency( Dependency dependency )
162    {
163        DependencyManagement management = null;
164
165        Object key = getKey( dependency.getArtifact() );
166
167        String version = managedVersions.get( key );
168        if ( version != null )
169        {
170            if ( management == null )
171            {
172                management = new DependencyManagement();
173            }
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 )
187                     && dependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) != null )
188            {
189                Map<String, String> properties =
190                    new HashMap<>( dependency.getArtifact().getProperties() );
191
192                properties.remove( ArtifactProperties.LOCAL_PATH );
193                management.setProperties( properties );
194            }
195        }
196
197        if ( ( scope != null && JavaScopes.SYSTEM.equals( scope ) )
198                 || ( scope == null && JavaScopes.SYSTEM.equals( dependency.getScope() ) ) )
199        {
200            String localPath = managedLocalPaths.get( key );
201            if ( localPath != null )
202            {
203                if ( management == null )
204                {
205                    management = new DependencyManagement();
206                }
207
208                Map<String, String> properties =
209                    new HashMap<>( dependency.getArtifact().getProperties() );
210
211                properties.put( ArtifactProperties.LOCAL_PATH, localPath );
212                management.setProperties( properties );
213            }
214        }
215
216        Boolean optional = managedOptionals.get( key );
217        if ( optional != null )
218        {
219            if ( management == null )
220            {
221                management = new DependencyManagement();
222            }
223            management.setOptional( optional );
224        }
225
226        Collection<Exclusion> exclusions = managedExclusions.get( key );
227        if ( exclusions != null )
228        {
229            if ( management == null )
230            {
231                management = new DependencyManagement();
232            }
233            Collection<Exclusion> result = new LinkedHashSet<>( dependency.getExclusions() );
234            result.addAll( exclusions );
235            management.setExclusions( result );
236        }
237
238        return management;
239    }
240
241    private Object getKey( Artifact a )
242    {
243        return new Key( a );
244    }
245
246    @Override
247    public boolean equals( final Object obj )
248    {
249        boolean equal = obj instanceof DefaultDependencyManager;
250
251        if ( equal )
252        {
253            final DefaultDependencyManager that = (DefaultDependencyManager) obj;
254            equal = Objects.equals( managedVersions, that.managedVersions )
255                        && Objects.equals( managedScopes, that.managedScopes )
256                        && Objects.equals( managedOptionals, that.managedOptionals )
257                        && Objects.equals( managedExclusions, that.managedExclusions );
258
259        }
260
261        return equal;
262    }
263
264    @Override
265    public int hashCode()
266    {
267        if ( hashCode == 0 )
268        {
269            hashCode = Objects.hash( managedVersions, managedScopes, managedOptionals, managedExclusions );
270        }
271        return hashCode;
272    }
273
274    static class Key
275    {
276
277        private final Artifact artifact;
278
279        private final int hashCode;
280
281        Key( final Artifact artifact )
282        {
283            this.artifact = artifact;
284            this.hashCode = Objects.hash( artifact.getGroupId(), artifact.getArtifactId() );
285        }
286
287        @Override
288        public boolean equals( final Object obj )
289        {
290            boolean equal = obj instanceof Key;
291
292            if ( equal )
293            {
294                final Key that = (Key) obj;
295                return Objects.equals( artifact.getArtifactId(), that.artifact.getArtifactId() )
296                           && Objects.equals( artifact.getGroupId(), that.artifact.getGroupId() )
297                           && Objects.equals( artifact.getExtension(), that.artifact.getExtension() )
298                           && Objects.equals( artifact.getClassifier(), that.artifact.getClassifier() );
299
300            }
301
302            return equal;
303        }
304
305        @Override
306        public int hashCode()
307        {
308            return this.hashCode;
309        }
310
311    }
312
313}