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;
027
028import org.eclipse.aether.artifact.Artifact;
029import org.eclipse.aether.artifact.ArtifactProperties;
030import org.eclipse.aether.collection.DependencyCollectionContext;
031import org.eclipse.aether.collection.DependencyManagement;
032import org.eclipse.aether.collection.DependencyManager;
033import org.eclipse.aether.graph.Dependency;
034import org.eclipse.aether.graph.Exclusion;
035import org.eclipse.aether.util.artifact.JavaScopes;
036
037import static java.util.Objects.requireNonNull;
038
039/**
040 * A dependency manager that mimics the way Maven 2.x works.
041 */
042public final class ClassicDependencyManager
043    implements DependencyManager
044{
045
046    private final int depth;
047
048    private final Map<Object, String> managedVersions;
049
050    private final Map<Object, String> managedScopes;
051
052    private final Map<Object, Boolean> managedOptionals;
053
054    private final Map<Object, String> managedLocalPaths;
055
056    private final Map<Object, Collection<Exclusion>> managedExclusions;
057
058    private int hashCode;
059
060    /**
061     * Creates a new dependency manager without any management information.
062     */
063    public ClassicDependencyManager()
064    {
065        this( 0, Collections.<Object, String>emptyMap(), Collections.<Object, String>emptyMap(),
066              Collections.<Object, Boolean>emptyMap(), Collections.<Object, String>emptyMap(),
067              Collections.<Object, Collection<Exclusion>>emptyMap() );
068    }
069
070    private ClassicDependencyManager( int depth, Map<Object, String> managedVersions,
071                                      Map<Object, String> managedScopes, Map<Object, Boolean> managedOptionals,
072                                      Map<Object, String> managedLocalPaths,
073                                      Map<Object, Collection<Exclusion>> managedExclusions )
074    {
075        this.depth = depth;
076        this.managedVersions = managedVersions;
077        this.managedScopes = managedScopes;
078        this.managedOptionals = managedOptionals;
079        this.managedLocalPaths = managedLocalPaths;
080        this.managedExclusions = managedExclusions;
081    }
082
083    public DependencyManager deriveChildManager( DependencyCollectionContext context )
084    {
085        requireNonNull( context, "context cannot be null" );
086        if ( depth >= 2 )
087        {
088            return this;
089        }
090        else if ( depth == 1 )
091        {
092            return new ClassicDependencyManager( depth + 1, managedVersions, managedScopes, managedOptionals,
093                                                 managedLocalPaths, managedExclusions );
094        }
095
096        Map<Object, String> managedVersions = this.managedVersions;
097        Map<Object, String> managedScopes = this.managedScopes;
098        Map<Object, Boolean> managedOptionals = this.managedOptionals;
099        Map<Object, String> managedLocalPaths = this.managedLocalPaths;
100        Map<Object, Collection<Exclusion>> managedExclusions = this.managedExclusions;
101
102        for ( Dependency managedDependency : context.getManagedDependencies() )
103        {
104            Artifact artifact = managedDependency.getArtifact();
105            Object key = getKey( artifact );
106
107            String version = artifact.getVersion();
108            if ( version.length() > 0 && !managedVersions.containsKey( key ) )
109            {
110                if ( managedVersions == this.managedVersions )
111                {
112                    managedVersions = new HashMap<>( this.managedVersions );
113                }
114                managedVersions.put( key, version );
115            }
116
117            String scope = managedDependency.getScope();
118            if ( scope.length() > 0 && !managedScopes.containsKey( key ) )
119            {
120                if ( managedScopes == this.managedScopes )
121                {
122                    managedScopes = new HashMap<>( this.managedScopes );
123                }
124                managedScopes.put( key, scope );
125            }
126
127            Boolean optional = managedDependency.getOptional();
128            if ( optional != null && !managedOptionals.containsKey( key ) )
129            {
130                if ( managedOptionals == this.managedOptionals )
131                {
132                    managedOptionals = new HashMap<>( this.managedOptionals );
133                }
134                managedOptionals.put( key, optional );
135            }
136
137            String localPath = managedDependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null );
138            if ( localPath != null && !managedLocalPaths.containsKey( key ) )
139            {
140                if ( managedLocalPaths == this.managedLocalPaths )
141                {
142                    managedLocalPaths = new HashMap<>( this.managedLocalPaths );
143                }
144                managedLocalPaths.put( key, localPath );
145            }
146
147            Collection<Exclusion> exclusions = managedDependency.getExclusions();
148            if ( !exclusions.isEmpty() )
149            {
150                if ( managedExclusions == this.managedExclusions )
151                {
152                    managedExclusions = new HashMap<>( this.managedExclusions );
153                }
154                Collection<Exclusion> managed = managedExclusions.computeIfAbsent( key, k -> new LinkedHashSet<>() );
155                managed.addAll( exclusions );
156            }
157        }
158
159        return new ClassicDependencyManager( depth + 1, managedVersions, managedScopes, managedOptionals,
160                                             managedLocalPaths, managedExclusions );
161    }
162
163    public DependencyManagement manageDependency( Dependency dependency )
164    {
165        requireNonNull( dependency, "dependency cannot be null" );
166        DependencyManagement management = null;
167
168        Object key = getKey( dependency.getArtifact() );
169
170        if ( depth >= 2 )
171        {
172            String version = managedVersions.get( key );
173            if ( version != null )
174            {
175                management = new DependencyManagement();
176                management.setVersion( version );
177            }
178
179            String scope = managedScopes.get( key );
180            if ( scope != null )
181            {
182                if ( management == null )
183                {
184                    management = new DependencyManagement();
185                }
186                management.setScope( scope );
187
188                if ( !JavaScopes.SYSTEM.equals( scope )
189                    && dependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) != null )
190                {
191                    Map<String, String> properties =
192                        new HashMap<>( dependency.getArtifact().getProperties() );
193                    properties.remove( ArtifactProperties.LOCAL_PATH );
194                    management.setProperties( properties );
195                }
196            }
197
198            if ( ( JavaScopes.SYSTEM.equals( scope ) )
199                || ( scope == null && JavaScopes.SYSTEM.equals( dependency.getScope() ) ) )
200            {
201                String localPath = managedLocalPaths.get( key );
202                if ( localPath != null )
203                {
204                    if ( management == null )
205                    {
206                        management = new DependencyManagement();
207                    }
208                    Map<String, String> properties =
209                        new HashMap<>( dependency.getArtifact().getProperties() );
210                    properties.put( ArtifactProperties.LOCAL_PATH, localPath );
211                    management.setProperties( properties );
212                }
213            }
214
215            Boolean optional = managedOptionals.get( key );
216            if ( optional != null )
217            {
218                if ( management == null )
219                {
220                    management = new DependencyManagement();
221                }
222                management.setOptional( optional );
223            }
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( Object obj )
248    {
249        if ( this == obj )
250        {
251            return true;
252        }
253        else if ( null == obj || !getClass().equals( obj.getClass() ) )
254        {
255            return false;
256        }
257
258        ClassicDependencyManager that = (ClassicDependencyManager) obj;
259        return depth == that.depth && managedVersions.equals( that.managedVersions )
260            && managedScopes.equals( that.managedScopes ) && managedOptionals.equals( that.managedOptionals )
261            && managedExclusions.equals( that.managedExclusions );
262    }
263
264    @Override
265    public int hashCode()
266    {
267        if ( hashCode == 0 )
268        {
269            int hash = 17;
270            hash = hash * 31 + depth;
271            hash = hash * 31 + managedVersions.hashCode();
272            hash = hash * 31 + managedScopes.hashCode();
273            hash = hash * 31 + managedOptionals.hashCode();
274            hash = hash * 31 + managedExclusions.hashCode();
275            hashCode = hash;
276        }
277        return hashCode;
278    }
279
280    static class Key
281    {
282
283        private final Artifact artifact;
284
285        private final int hashCode;
286
287        Key( Artifact artifact )
288        {
289            this.artifact = artifact;
290
291            int hash = 17;
292            hash = hash * 31 + artifact.getGroupId().hashCode();
293            hash = hash * 31 + artifact.getArtifactId().hashCode();
294            hashCode = hash;
295        }
296
297        @Override
298        public boolean equals( Object obj )
299        {
300            if ( obj == this )
301            {
302                return true;
303            }
304            else if ( !( obj instanceof Key ) )
305            {
306                return false;
307            }
308            Key that = (Key) obj;
309            return artifact.getArtifactId().equals( that.artifact.getArtifactId() )
310                && artifact.getGroupId().equals( that.artifact.getGroupId() )
311                && artifact.getExtension().equals( that.artifact.getExtension() )
312                && artifact.getClassifier().equals( that.artifact.getClassifier() );
313        }
314
315        @Override
316        public int hashCode()
317        {
318            return hashCode;
319        }
320
321    }
322
323}