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