001package org.eclipse.aether.internal.impl.collect;
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 */
021import static java.util.Objects.requireNonNull;
022import static org.junit.Assert.assertEquals;
023import static org.junit.Assert.assertNotEquals;
024import static org.junit.Assert.assertNotNull;
025import static org.junit.Assert.assertNull;
026import static org.junit.Assert.assertSame;
027import static org.junit.Assert.assertTrue;
028import static org.junit.Assert.fail;
029
030import java.io.IOException;
031import java.util.ArrayList;
032import java.util.Arrays;
033import java.util.Collection;
034import java.util.Collections;
035import java.util.HashMap;
036import java.util.Iterator;
037import java.util.LinkedList;
038import java.util.List;
039import java.util.Map;
040
041import org.eclipse.aether.DefaultRepositorySystemSession;
042import org.eclipse.aether.RepositorySystemSession;
043import org.eclipse.aether.artifact.Artifact;
044import org.eclipse.aether.artifact.ArtifactProperties;
045import org.eclipse.aether.artifact.DefaultArtifact;
046import org.eclipse.aether.collection.CollectRequest;
047import org.eclipse.aether.collection.CollectResult;
048import org.eclipse.aether.collection.DependencyCollectionContext;
049import org.eclipse.aether.collection.DependencyCollectionException;
050import org.eclipse.aether.collection.DependencyManagement;
051import org.eclipse.aether.collection.DependencyManager;
052import org.eclipse.aether.graph.DefaultDependencyNode;
053import org.eclipse.aether.graph.Dependency;
054import org.eclipse.aether.graph.DependencyCycle;
055import org.eclipse.aether.graph.DependencyNode;
056import org.eclipse.aether.graph.Exclusion;
057import org.eclipse.aether.impl.ArtifactDescriptorReader;
058import org.eclipse.aether.internal.impl.IniArtifactDescriptorReader;
059import org.eclipse.aether.internal.impl.StubRemoteRepositoryManager;
060import org.eclipse.aether.internal.impl.StubVersionRangeResolver;
061import org.eclipse.aether.internal.test.util.DependencyGraphParser;
062import org.eclipse.aether.internal.test.util.TestUtils;
063import org.eclipse.aether.repository.RemoteRepository;
064import org.eclipse.aether.resolution.ArtifactDescriptorException;
065import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
066import org.eclipse.aether.resolution.ArtifactDescriptorResult;
067import org.eclipse.aether.util.artifact.ArtifactIdUtils;
068import org.eclipse.aether.util.graph.manager.ClassicDependencyManager;
069import org.eclipse.aether.util.graph.manager.DefaultDependencyManager;
070import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
071import org.eclipse.aether.util.graph.manager.TransitiveDependencyManager;
072import org.eclipse.aether.util.graph.version.HighestVersionFilter;
073import org.junit.Before;
074import org.junit.Test;
075
076/**
077 */
078public class DefaultDependencyCollectorTest
079{
080
081    private DefaultDependencyCollector collector;
082
083    private DefaultRepositorySystemSession session;
084
085    private DependencyGraphParser parser;
086
087    private RemoteRepository repository;
088
089    private IniArtifactDescriptorReader newReader( String prefix )
090    {
091        return new IniArtifactDescriptorReader( "artifact-descriptions/" + prefix );
092    }
093
094    private Dependency newDep( String coords )
095    {
096        return newDep( coords, "" );
097    }
098
099    private Dependency newDep( String coords, String scope )
100    {
101        return new Dependency( new DefaultArtifact( coords ), scope );
102    }
103
104    @Before
105    public void setup()
106    {
107        session = TestUtils.newSession();
108
109        collector = new DefaultDependencyCollector();
110        collector.setArtifactDescriptorReader( newReader( "" ) );
111        collector.setVersionRangeResolver( new StubVersionRangeResolver() );
112        collector.setRemoteRepositoryManager( new StubRemoteRepositoryManager() );
113
114        parser = new DependencyGraphParser( "artifact-descriptions/" );
115
116        repository = new RemoteRepository.Builder( "id", "default", "file:///" ).build();
117    }
118
119    private static void assertEqualSubtree( DependencyNode expected, DependencyNode actual )
120    {
121        assertEqualSubtree( expected, actual, new LinkedList<DependencyNode>() );
122    }
123
124    private static void assertEqualSubtree( DependencyNode expected, DependencyNode actual,
125                                            LinkedList<DependencyNode> parents )
126    {
127        assertEquals( "path: " + parents, expected.getDependency(), actual.getDependency() );
128
129        if ( actual.getDependency() != null )
130        {
131            Artifact artifact = actual.getDependency().getArtifact();
132            for ( DependencyNode parent : parents )
133            {
134                if ( parent.getDependency() != null && artifact.equals( parent.getDependency().getArtifact() ) )
135                {
136                    return;
137                }
138            }
139        }
140
141        parents.addLast( expected );
142
143        assertEquals( "path: " + parents + ", expected: " + expected.getChildren() + ", actual: "
144                          + actual.getChildren(), expected.getChildren().size(), actual.getChildren().size() );
145
146        Iterator<DependencyNode> iterator1 = expected.getChildren().iterator();
147        Iterator<DependencyNode> iterator2 = actual.getChildren().iterator();
148
149        while ( iterator1.hasNext() )
150        {
151            assertEqualSubtree( iterator1.next(), iterator2.next(), parents );
152        }
153
154        parents.removeLast();
155    }
156
157    private Dependency dep( DependencyNode root, int... coords )
158    {
159        return path( root, coords ).getDependency();
160    }
161
162    private DependencyNode path( DependencyNode root, int... coords )
163    {
164        try
165        {
166            DependencyNode node = root;
167            for ( int coord : coords )
168            {
169                node = node.getChildren().get( coord );
170            }
171
172            return node;
173        }
174        catch ( IndexOutOfBoundsException | NullPointerException e )
175        {
176            throw new IllegalArgumentException( "illegal coordinates for child", e );
177        }
178    }
179
180    @Test
181    public void testSimpleCollection()
182        throws DependencyCollectionException
183    {
184        Dependency dependency = newDep( "gid:aid:ext:ver", "compile" );
185        CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
186        CollectResult result = collector.collectDependencies( session, request );
187
188        assertEquals( 0, result.getExceptions().size() );
189
190        DependencyNode root = result.getRoot();
191        Dependency newDependency = root.getDependency();
192
193        assertEquals( dependency, newDependency );
194        assertEquals( dependency.getArtifact(), newDependency.getArtifact() );
195
196        assertEquals( 1, root.getChildren().size() );
197
198        Dependency expect = newDep( "gid:aid2:ext:ver", "compile" );
199        assertEquals( expect, root.getChildren().get( 0 ).getDependency() );
200    }
201
202    @Test
203    public void testMissingDependencyDescription()
204    {
205        CollectRequest request =
206            new CollectRequest( newDep( "missing:description:ext:ver" ), Arrays.asList( repository ) );
207        try
208        {
209            collector.collectDependencies( session, request );
210            fail( "expected exception" );
211        }
212        catch ( DependencyCollectionException e )
213        {
214            CollectResult result = e.getResult();
215            assertSame( request, result.getRequest() );
216            assertNotNull( result.getExceptions() );
217            assertEquals( 1, result.getExceptions().size() );
218
219            assertTrue( result.getExceptions().get( 0 ) instanceof ArtifactDescriptorException );
220
221            assertEquals( request.getRoot(), result.getRoot().getDependency() );
222        }
223    }
224
225    @Test
226    public void testDuplicates()
227        throws DependencyCollectionException
228    {
229        Dependency dependency = newDep( "duplicate:transitive:ext:dependency" );
230        CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
231
232        CollectResult result = collector.collectDependencies( session, request );
233
234        assertEquals( 0, result.getExceptions().size() );
235
236        DependencyNode root = result.getRoot();
237        Dependency newDependency = root.getDependency();
238
239        assertEquals( dependency, newDependency );
240        assertEquals( dependency.getArtifact(), newDependency.getArtifact() );
241
242        assertEquals( 2, root.getChildren().size() );
243
244        Dependency dep = newDep( "gid:aid:ext:ver", "compile" );
245        assertEquals( dep, dep( root, 0 ) );
246
247        dep = newDep( "gid:aid2:ext:ver", "compile" );
248        assertEquals( dep, dep( root, 1 ) );
249        assertEquals( dep, dep( root, 0, 0 ) );
250        assertEquals( dep( root, 1 ), dep( root, 0, 0 ) );
251    }
252
253    @Test
254    public void testEqualSubtree()
255        throws IOException, DependencyCollectionException
256    {
257        DependencyNode root = parser.parseResource( "expectedSubtreeComparisonResult.txt" );
258        Dependency dependency = root.getDependency();
259        CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
260
261        CollectResult result = collector.collectDependencies( session, request );
262        assertEqualSubtree( root, result.getRoot() );
263    }
264
265    @Test
266    public void testCyclicDependencies()
267        throws Exception
268    {
269        DependencyNode root = parser.parseResource( "cycle.txt" );
270        CollectRequest request = new CollectRequest( root.getDependency(), Arrays.asList( repository ) );
271        CollectResult result = collector.collectDependencies( session, request );
272        assertEqualSubtree( root, result.getRoot() );
273    }
274
275    @Test
276    public void testCyclicDependenciesBig()
277        throws Exception
278    {
279        CollectRequest request = new CollectRequest( newDep( "1:2:pom:5.50-SNAPSHOT" ), Arrays.asList( repository ) );
280        collector.setArtifactDescriptorReader( newReader( "cycle-big/" ) );
281        CollectResult result = collector.collectDependencies( session, request );
282        assertNotNull( result.getRoot() );
283        // we only care about the performance here, this test must not hang or run out of mem
284    }
285
286    @Test
287    public void testCyclicProjects()
288        throws Exception
289    {
290        CollectRequest request = new CollectRequest( newDep( "test:a:2" ), Arrays.asList( repository ) );
291        collector.setArtifactDescriptorReader( newReader( "versionless-cycle/" ) );
292        CollectResult result = collector.collectDependencies( session, request );
293        DependencyNode root = result.getRoot();
294        DependencyNode a1 = path( root, 0, 0 );
295        assertEquals( "a", a1.getArtifact().getArtifactId() );
296        assertEquals( "1", a1.getArtifact().getVersion() );
297        for ( DependencyNode child : a1.getChildren() )
298        {
299            assertNotEquals( "1", child.getArtifact().getVersion() );
300        }
301
302        assertEquals( 1, result.getCycles().size() );
303        DependencyCycle cycle = result.getCycles().get( 0 );
304        assertEquals( Arrays.asList(), cycle.getPrecedingDependencies() );
305        assertEquals( Arrays.asList( root.getDependency(), path( root, 0 ).getDependency(), a1.getDependency() ),
306                      cycle.getCyclicDependencies() );
307    }
308
309    @Test
310    public void testCyclicProjects_ConsiderLabelOfRootlessGraph()
311        throws Exception
312    {
313        Dependency dep = newDep( "gid:aid:ver", "compile" );
314        CollectRequest request =
315            new CollectRequest().addDependency( dep ).addRepository( repository ).setRootArtifact( dep.getArtifact() );
316        CollectResult result = collector.collectDependencies( session, request );
317        DependencyNode root = result.getRoot();
318        DependencyNode a1 = root.getChildren().get( 0 );
319        assertEquals( "aid", a1.getArtifact().getArtifactId() );
320        assertEquals( "ver", a1.getArtifact().getVersion() );
321        DependencyNode a2 = a1.getChildren().get( 0 );
322        assertEquals( "aid2", a2.getArtifact().getArtifactId() );
323        assertEquals( "ver", a2.getArtifact().getVersion() );
324
325        assertEquals( 1, result.getCycles().size() );
326        DependencyCycle cycle = result.getCycles().get( 0 );
327        assertEquals( Arrays.asList(), cycle.getPrecedingDependencies() );
328        assertEquals( Arrays.asList( new Dependency( dep.getArtifact(), null ), a1.getDependency() ),
329                      cycle.getCyclicDependencies() );
330    }
331
332    @Test
333    public void testPartialResultOnError()
334        throws IOException
335    {
336        DependencyNode root = parser.parseResource( "expectedPartialSubtreeOnError.txt" );
337
338        Dependency dependency = root.getDependency();
339        CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
340
341        CollectResult result;
342        try
343        {
344            result = collector.collectDependencies( session, request );
345            fail( "expected exception " );
346        }
347        catch ( DependencyCollectionException e )
348        {
349            result = e.getResult();
350
351            assertSame( request, result.getRequest() );
352            assertNotNull( result.getExceptions() );
353            assertEquals( 1, result.getExceptions().size() );
354
355            assertTrue( result.getExceptions().get( 0 ) instanceof ArtifactDescriptorException );
356
357            assertEqualSubtree( root, result.getRoot() );
358        }
359    }
360
361    @Test
362    public void testCollectMultipleDependencies()
363        throws DependencyCollectionException
364    {
365        Dependency root1 = newDep( "gid:aid:ext:ver", "compile" );
366        Dependency root2 = newDep( "gid:aid2:ext:ver", "compile" );
367        List<Dependency> dependencies = Arrays.asList( root1, root2 );
368        CollectRequest request = new CollectRequest( dependencies, null, Arrays.asList( repository ) );
369        CollectResult result = collector.collectDependencies( session, request );
370
371        assertEquals( 0, result.getExceptions().size() );
372        assertEquals( 2, result.getRoot().getChildren().size() );
373        assertEquals( root1, dep( result.getRoot(), 0 ) );
374
375        assertEquals( 1, path( result.getRoot(), 0 ).getChildren().size() );
376        assertEquals( root2, dep( result.getRoot(), 0, 0 ) );
377
378        assertEquals( 0, path( result.getRoot(), 1 ).getChildren().size() );
379        assertEquals( root2, dep( result.getRoot(), 1 ) );
380    }
381
382    @Test
383    public void testArtifactDescriptorResolutionNotRestrictedToRepoHostingSelectedVersion()
384        throws Exception
385    {
386        RemoteRepository repo2 = new RemoteRepository.Builder( "test", "default", "file:///" ).build();
387
388        final List<RemoteRepository> repos = new ArrayList<>();
389
390        collector.setArtifactDescriptorReader( new ArtifactDescriptorReader()
391        {
392            public ArtifactDescriptorResult readArtifactDescriptor( RepositorySystemSession session,
393                                                                    ArtifactDescriptorRequest request )
394            {
395                repos.addAll( request.getRepositories() );
396                return new ArtifactDescriptorResult( request );
397            }
398        } );
399
400        List<Dependency> dependencies = Arrays.asList( newDep( "verrange:parent:jar:1[1,)", "compile" ) );
401        CollectRequest request = new CollectRequest( dependencies, null, Arrays.asList( repository, repo2 ) );
402        CollectResult result = collector.collectDependencies( session, request );
403
404        assertEquals( 0, result.getExceptions().size() );
405        assertEquals( 2, repos.size() );
406        assertEquals( "id", repos.get( 0 ).getId() );
407        assertEquals( "test", repos.get( 1 ).getId() );
408    }
409
410    @Test
411    public void testManagedVersionScope()
412        throws DependencyCollectionException
413    {
414        Dependency dependency = newDep( "managed:aid:ext:ver" );
415        CollectRequest request = new CollectRequest( dependency, Arrays.asList( repository ) );
416
417        session.setDependencyManager( new ClassicDependencyManager() );
418
419        CollectResult result = collector.collectDependencies( session, request );
420
421        assertEquals( 0, result.getExceptions().size() );
422
423        DependencyNode root = result.getRoot();
424
425        assertEquals( dependency, dep( root ) );
426        assertEquals( dependency.getArtifact(), dep( root ).getArtifact() );
427
428        assertEquals( 1, root.getChildren().size() );
429        Dependency expect = newDep( "gid:aid:ext:ver", "compile" );
430        assertEquals( expect, dep( root, 0 ) );
431
432        assertEquals( 1, path( root, 0 ).getChildren().size() );
433        expect = newDep( "gid:aid2:ext:managedVersion", "managedScope" );
434        assertEquals( expect, dep( root, 0, 0 ) );
435    }
436
437    @Test
438    public void testDependencyManagement()
439        throws IOException, DependencyCollectionException
440    {
441        collector.setArtifactDescriptorReader( newReader( "managed/" ) );
442
443        DependencyNode root = parser.parseResource( "expectedSubtreeComparisonResult.txt" );
444        TestDependencyManager depMgmt = new TestDependencyManager();
445        depMgmt.add( dep( root, 0 ), "managed", null, null );
446        depMgmt.add( dep( root, 0, 1 ), "managed", "managed", null );
447        depMgmt.add( dep( root, 1 ), null, null, "managed" );
448        session.setDependencyManager( depMgmt );
449
450        // collect result will differ from expectedSubtreeComparisonResult.txt
451        // set localPath -> no dependency traversal
452        CollectRequest request = new CollectRequest( dep( root ), Arrays.asList( repository ) );
453        CollectResult result = collector.collectDependencies( session, request );
454
455        DependencyNode node = result.getRoot();
456        assertEquals( "managed", dep( node, 0, 1 ).getArtifact().getVersion() );
457        assertEquals( "managed", dep( node, 0, 1 ).getScope() );
458
459        assertEquals( "managed", dep( node, 1 ).getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) );
460        assertEquals( "managed", dep( node, 0, 0 ).getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ) );
461    }
462
463    @Test
464    public void testDependencyManagement_VerboseMode()
465        throws Exception
466    {
467        String depId = "gid:aid2:ext";
468        TestDependencyManager depMgmt = new TestDependencyManager();
469        depMgmt.version( depId, "managedVersion" );
470        depMgmt.scope( depId, "managedScope" );
471        depMgmt.optional( depId, Boolean.TRUE );
472        depMgmt.path( depId, "managedPath" );
473        depMgmt.exclusions( depId, new Exclusion( "gid", "aid", "*", "*" ) );
474        session.setDependencyManager( depMgmt );
475        session.setConfigProperty( DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE );
476
477        CollectRequest request = new CollectRequest().setRoot( newDep( "gid:aid:ver" ) );
478        CollectResult result = collector.collectDependencies( session, request );
479        DependencyNode node = result.getRoot().getChildren().get( 0 );
480        assertEquals( DependencyNode.MANAGED_VERSION | DependencyNode.MANAGED_SCOPE | DependencyNode.MANAGED_OPTIONAL
481            | DependencyNode.MANAGED_PROPERTIES | DependencyNode.MANAGED_EXCLUSIONS, node.getManagedBits() );
482        assertEquals( "ver", DependencyManagerUtils.getPremanagedVersion( node ) );
483        assertEquals( "compile", DependencyManagerUtils.getPremanagedScope( node ) );
484        assertEquals( Boolean.FALSE, DependencyManagerUtils.getPremanagedOptional( node ) );
485    }
486
487    @Test
488    public void testDependencyManagement_TransitiveDependencyManager()
489            throws DependencyCollectionException, IOException
490    {
491        collector.setArtifactDescriptorReader( newReader( "managed/" ) );
492        parser = new DependencyGraphParser( "artifact-descriptions/managed/" );
493        session.setDependencyManager( new TransitiveDependencyManager() );
494        final Dependency root = newDep( "gid:root:ext:ver", "compile" );
495        CollectRequest request = new CollectRequest( root, Collections.singletonList( repository ) );
496        request.addManagedDependency( newDep( "gid:root:ext:must-retain-core-management" ) );
497        CollectResult result = collector.collectDependencies( session, request );
498
499        final DependencyNode expectedTree = parser.parseResource( "management-tree.txt" );
500        assertEqualSubtree( expectedTree, result.getRoot() );
501
502        // Same test for root artifact (POM) request.
503        final CollectRequest rootArtifactRequest = new CollectRequest();
504        rootArtifactRequest.setRepositories( Collections.singletonList( repository ) );
505        rootArtifactRequest.setRootArtifact( new DefaultArtifact( "gid:root:ext:ver" ) );
506        rootArtifactRequest.addDependency( newDep( "gid:direct:ext:ver", "compile" ) );
507        rootArtifactRequest.addManagedDependency( newDep( "gid:root:ext:must-retain-core-management" ) );
508        rootArtifactRequest.addManagedDependency( newDep( "gid:direct:ext:must-retain-core-management" ) );
509        rootArtifactRequest.addManagedDependency( newDep( "gid:transitive-1:ext:managed-by-root" ) );
510        session.setDependencyManager( new TransitiveDependencyManager() );
511        result = collector.collectDependencies( session, rootArtifactRequest );
512        assertEqualSubtree( expectedTree, toDependencyResult( result.getRoot(), "compile", null ) );
513    }
514
515    @Test
516    public void testDependencyManagement_DefaultDependencyManager()
517        throws DependencyCollectionException, IOException
518    {
519        collector.setArtifactDescriptorReader( newReader( "managed/" ) );
520        parser = new DependencyGraphParser( "artifact-descriptions/managed/" );
521        session.setDependencyManager( new DefaultDependencyManager() );
522        final Dependency root = newDep( "gid:root:ext:ver", "compile" );
523        CollectRequest request = new CollectRequest( root, Arrays.asList( repository ) );
524        request.addManagedDependency( newDep( "gid:root:ext:must-not-manage-root" ) );
525        request.addManagedDependency( newDep( "gid:direct:ext:managed-by-dominant-request" ) );
526        CollectResult result = collector.collectDependencies( session, request );
527
528        final DependencyNode expectedTree = parser.parseResource( "default-management-tree.txt" );
529        assertEqualSubtree( expectedTree, result.getRoot() );
530
531        // Same test for root artifact (POM) request.
532        final CollectRequest rootArtifactRequest = new CollectRequest();
533        rootArtifactRequest.setRepositories( Arrays.asList( repository ) );
534        rootArtifactRequest.setRootArtifact( new DefaultArtifact( "gid:root:ext:ver" ) );
535        rootArtifactRequest.addDependency( newDep( "gid:direct:ext:ver", "compile" ) );
536        rootArtifactRequest.addManagedDependency( newDep( "gid:root:ext:must-not-manage-root" ) );
537        rootArtifactRequest.addManagedDependency( newDep( "gid:direct:ext:managed-by-dominant-request" ) );
538        rootArtifactRequest.addManagedDependency( newDep( "gid:transitive-1:ext:managed-by-root" ) );
539        session.setDependencyManager( new DefaultDependencyManager() );
540        result = collector.collectDependencies( session, rootArtifactRequest );
541        assertEqualSubtree( expectedTree, toDependencyResult( result.getRoot(), "compile", null ) );
542    }
543
544    private DependencyNode toDependencyResult( final DependencyNode root, final String rootScope,
545                                               final Boolean optional )
546    {
547        // Make the root artifact resultion result a dependency resolution result for the subtree check.
548        assertNull( "Expected root artifact resolution result.", root.getDependency() );
549        final DefaultDependencyNode defaultNode =
550                new DefaultDependencyNode( new Dependency( root.getArtifact(), rootScope ) );
551
552        defaultNode.setChildren( root.getChildren() );
553
554        if ( optional != null )
555        {
556            defaultNode.setOptional( optional );
557        }
558
559        return defaultNode;
560    }
561
562    @Test
563    public void testVersionFilter()
564        throws Exception
565    {
566        session.setVersionFilter( new HighestVersionFilter() );
567        CollectRequest request = new CollectRequest().setRoot( newDep( "gid:aid:1" ) );
568        CollectResult result = collector.collectDependencies( session, request );
569        assertEquals( 1, result.getRoot().getChildren().size() );
570    }
571
572    static class TestDependencyManager
573        implements DependencyManager
574    {
575
576        private Map<String, String> versions = new HashMap<>();
577
578        private Map<String, String> scopes = new HashMap<>();
579
580        private Map<String, Boolean> optionals = new HashMap<>();
581
582        private Map<String, String> paths = new HashMap<>();
583
584        private Map<String, Collection<Exclusion>> exclusions = new HashMap<>();
585
586        public void add( Dependency d, String version, String scope, String localPath )
587        {
588            String id = toKey( d );
589            version( id, version );
590            scope( id, scope );
591            path( id, localPath );
592        }
593
594        public void version( String id, String version )
595        {
596            versions.put( id, version );
597        }
598
599        public void scope( String id, String scope )
600        {
601            scopes.put( id, scope );
602        }
603
604        public void optional( String id, Boolean optional )
605        {
606            optionals.put( id, optional );
607        }
608
609        public void path( String id, String path )
610        {
611            paths.put( id, path );
612        }
613
614        public void exclusions( String id, Exclusion... exclusions )
615        {
616            this.exclusions.put( id, exclusions != null ? Arrays.asList( exclusions ) : null );
617        }
618
619        public DependencyManagement manageDependency( Dependency dependency )
620        {
621            requireNonNull( dependency, "dependency cannot be null" );
622            String id = toKey( dependency );
623            DependencyManagement mgmt = new DependencyManagement();
624            mgmt.setVersion( versions.get( id ) );
625            mgmt.setScope( scopes.get( id ) );
626            mgmt.setOptional( optionals.get( id ) );
627            String path = paths.get( id );
628            if ( path != null )
629            {
630                mgmt.setProperties( Collections.singletonMap( ArtifactProperties.LOCAL_PATH, path ) );
631            }
632            mgmt.setExclusions( exclusions.get( id ) );
633            return mgmt;
634        }
635
636        private String toKey( Dependency dependency )
637        {
638            return ArtifactIdUtils.toVersionlessId( dependency.getArtifact() );
639        }
640
641        public DependencyManager deriveChildManager( DependencyCollectionContext context )
642        {
643            requireNonNull( context, "context cannot be null" );
644            return this;
645        }
646
647    }
648
649}