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 */
021
022import java.util.ArrayList;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.HashMap;
026import java.util.HashSet;
027import java.util.LinkedHashMap;
028import java.util.List;
029import java.util.Map;
030import static java.util.Objects.requireNonNull;
031
032import javax.inject.Inject;
033import javax.inject.Named;
034import javax.inject.Singleton;
035
036import org.eclipse.aether.DefaultRepositorySystemSession;
037import org.eclipse.aether.RepositoryException;
038import org.eclipse.aether.RepositorySystemSession;
039import org.eclipse.aether.RequestTrace;
040import org.eclipse.aether.artifact.Artifact;
041import org.eclipse.aether.artifact.ArtifactProperties;
042import org.eclipse.aether.collection.CollectRequest;
043import org.eclipse.aether.collection.CollectResult;
044import org.eclipse.aether.collection.DependencyCollectionException;
045import org.eclipse.aether.collection.DependencyGraphTransformer;
046import org.eclipse.aether.collection.DependencyManagement;
047import org.eclipse.aether.collection.DependencyManager;
048import org.eclipse.aether.collection.DependencySelector;
049import org.eclipse.aether.collection.DependencyTraverser;
050import org.eclipse.aether.collection.VersionFilter;
051import org.eclipse.aether.graph.DefaultDependencyNode;
052import org.eclipse.aether.graph.Dependency;
053import org.eclipse.aether.graph.DependencyNode;
054import org.eclipse.aether.graph.Exclusion;
055import org.eclipse.aether.impl.ArtifactDescriptorReader;
056import org.eclipse.aether.impl.DependencyCollector;
057import org.eclipse.aether.impl.RemoteRepositoryManager;
058import org.eclipse.aether.impl.VersionRangeResolver;
059import org.eclipse.aether.repository.ArtifactRepository;
060import org.eclipse.aether.repository.RemoteRepository;
061import org.eclipse.aether.resolution.ArtifactDescriptorException;
062import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
063import org.eclipse.aether.resolution.ArtifactDescriptorResult;
064import org.eclipse.aether.resolution.VersionRangeRequest;
065import org.eclipse.aether.resolution.VersionRangeResolutionException;
066import org.eclipse.aether.resolution.VersionRangeResult;
067import org.eclipse.aether.spi.locator.Service;
068import org.eclipse.aether.spi.locator.ServiceLocator;
069import org.eclipse.aether.util.ConfigUtils;
070import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
071import org.eclipse.aether.util.graph.transformer.TransformationContextKeys;
072import org.eclipse.aether.version.Version;
073import org.slf4j.Logger;
074import org.slf4j.LoggerFactory;
075
076/**
077 */
078@Singleton
079@Named
080public class DefaultDependencyCollector
081    implements DependencyCollector, Service
082{
083
084    private static final String CONFIG_PROP_MAX_EXCEPTIONS = "aether.dependencyCollector.maxExceptions";
085
086    private static final int CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT = 50;
087
088    private static final String CONFIG_PROP_MAX_CYCLES = "aether.dependencyCollector.maxCycles";
089
090    private static final int CONFIG_PROP_MAX_CYCLES_DEFAULT = 10;
091
092    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultDependencyCollector.class );
093
094    private RemoteRepositoryManager remoteRepositoryManager;
095
096    private ArtifactDescriptorReader descriptorReader;
097
098    private VersionRangeResolver versionRangeResolver;
099
100    public DefaultDependencyCollector()
101    {
102        // enables default constructor
103    }
104
105    @Inject
106    DefaultDependencyCollector( RemoteRepositoryManager remoteRepositoryManager,
107                                ArtifactDescriptorReader artifactDescriptorReader,
108                                VersionRangeResolver versionRangeResolver )
109    {
110        setRemoteRepositoryManager( remoteRepositoryManager );
111        setArtifactDescriptorReader( artifactDescriptorReader );
112        setVersionRangeResolver( versionRangeResolver );
113    }
114
115    public void initService( ServiceLocator locator )
116    {
117        setRemoteRepositoryManager( locator.getService( RemoteRepositoryManager.class ) );
118        setArtifactDescriptorReader( locator.getService( ArtifactDescriptorReader.class ) );
119        setVersionRangeResolver( locator.getService( VersionRangeResolver.class ) );
120    }
121
122    public DefaultDependencyCollector setRemoteRepositoryManager( RemoteRepositoryManager remoteRepositoryManager )
123    {
124        this.remoteRepositoryManager =
125                requireNonNull( remoteRepositoryManager, "remote repository provider cannot be null" );
126        return this;
127    }
128
129    public DefaultDependencyCollector setArtifactDescriptorReader( ArtifactDescriptorReader artifactDescriptorReader )
130    {
131        descriptorReader = requireNonNull( artifactDescriptorReader, "artifact descriptor reader cannot be null" );
132        return this;
133    }
134
135    public DefaultDependencyCollector setVersionRangeResolver( VersionRangeResolver versionRangeResolver )
136    {
137        this.versionRangeResolver =
138                requireNonNull( versionRangeResolver, "version range resolver cannot be null" );
139        return this;
140    }
141
142    @SuppressWarnings( "checkstyle:methodlength" )
143    public CollectResult collectDependencies( RepositorySystemSession session, CollectRequest request )
144        throws DependencyCollectionException
145    {
146        session = optimizeSession( session );
147
148        RequestTrace trace = RequestTrace.newChild( request.getTrace(), request );
149
150        CollectResult result = new CollectResult( request );
151
152        DependencySelector depSelector = session.getDependencySelector();
153        DependencyManager depManager = session.getDependencyManager();
154        DependencyTraverser depTraverser = session.getDependencyTraverser();
155        VersionFilter verFilter = session.getVersionFilter();
156
157        Dependency root = request.getRoot();
158        List<RemoteRepository> repositories = request.getRepositories();
159        List<Dependency> dependencies = request.getDependencies();
160        List<Dependency> managedDependencies = request.getManagedDependencies();
161
162        Map<String, Object> stats = new LinkedHashMap<>();
163        long time1 = System.nanoTime();
164
165        DefaultDependencyNode node;
166        if ( root != null )
167        {
168            List<? extends Version> versions;
169            VersionRangeResult rangeResult;
170            try
171            {
172                VersionRangeRequest rangeRequest =
173                    new VersionRangeRequest( root.getArtifact(), request.getRepositories(),
174                                             request.getRequestContext() );
175                rangeRequest.setTrace( trace );
176                rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
177                versions = filterVersions( root, rangeResult, verFilter, new DefaultVersionFilterContext( session ) );
178            }
179            catch ( VersionRangeResolutionException e )
180            {
181                result.addException( e );
182                throw new DependencyCollectionException( result, e.getMessage() );
183            }
184
185            Version version = versions.get( versions.size() - 1 );
186            root = root.setArtifact( root.getArtifact().setVersion( version.toString() ) );
187
188            ArtifactDescriptorResult descriptorResult;
189            try
190            {
191                ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
192                descriptorRequest.setArtifact( root.getArtifact() );
193                descriptorRequest.setRepositories( request.getRepositories() );
194                descriptorRequest.setRequestContext( request.getRequestContext() );
195                descriptorRequest.setTrace( trace );
196                if ( isLackingDescriptor( root.getArtifact() ) )
197                {
198                    descriptorResult = new ArtifactDescriptorResult( descriptorRequest );
199                }
200                else
201                {
202                    descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
203                }
204            }
205            catch ( ArtifactDescriptorException e )
206            {
207                result.addException( e );
208                throw new DependencyCollectionException( result, e.getMessage() );
209            }
210
211            root = root.setArtifact( descriptorResult.getArtifact() );
212
213            if ( !session.isIgnoreArtifactDescriptorRepositories() )
214            {
215                repositories = remoteRepositoryManager.aggregateRepositories( session, repositories,
216                                                                              descriptorResult.getRepositories(),
217                                                                              true );
218            }
219            dependencies = mergeDeps( dependencies, descriptorResult.getDependencies() );
220            managedDependencies = mergeDeps( managedDependencies, descriptorResult.getManagedDependencies() );
221
222            node = new DefaultDependencyNode( root );
223            node.setRequestContext( request.getRequestContext() );
224            node.setRelocations( descriptorResult.getRelocations() );
225            node.setVersionConstraint( rangeResult.getVersionConstraint() );
226            node.setVersion( version );
227            node.setAliases( descriptorResult.getAliases() );
228            node.setRepositories( request.getRepositories() );
229        }
230        else
231        {
232            node = new DefaultDependencyNode( request.getRootArtifact() );
233            node.setRequestContext( request.getRequestContext() );
234            node.setRepositories( request.getRepositories() );
235        }
236
237        result.setRoot( node );
238
239        boolean traverse = root == null || depTraverser == null || depTraverser.traverseDependency( root );
240        String errorPath = null;
241        if ( traverse && !dependencies.isEmpty() )
242        {
243            DataPool pool = new DataPool( session );
244
245            NodeStack nodes = new NodeStack();
246            nodes.push( node );
247
248            DefaultDependencyCollectionContext context =
249                new DefaultDependencyCollectionContext( session, request.getRootArtifact(), root, managedDependencies );
250
251            DefaultVersionFilterContext versionContext = new DefaultVersionFilterContext( session );
252
253            Args args = new Args( session, trace, pool, nodes, context, versionContext, request );
254            Results results = new Results( result, session );
255
256            process( args, results, dependencies, repositories,
257                     depSelector != null ? depSelector.deriveChildSelector( context ) : null,
258                     depManager != null ? depManager.deriveChildManager( context ) : null,
259                     depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null,
260                     verFilter != null ? verFilter.deriveChildFilter( context ) : null );
261
262            errorPath = results.errorPath;
263        }
264
265        long time2 = System.nanoTime();
266
267        DependencyGraphTransformer transformer = session.getDependencyGraphTransformer();
268        if ( transformer != null )
269        {
270            try
271            {
272                DefaultDependencyGraphTransformationContext context =
273                    new DefaultDependencyGraphTransformationContext( session );
274                context.put( TransformationContextKeys.STATS, stats );
275                result.setRoot( transformer.transformGraph( node, context ) );
276            }
277            catch ( RepositoryException e )
278            {
279                result.addException( e );
280            }
281        }
282
283        long time3 = System.nanoTime();
284        stats.put( "DefaultDependencyCollector.collectTime", time2 - time1 );
285        stats.put( "DefaultDependencyCollector.transformTime", time3 - time2 );
286        LOGGER.debug( "Dependency collection stats {}", stats );
287
288        if ( errorPath != null )
289        {
290            throw new DependencyCollectionException( result, "Failed to collect dependencies at " + errorPath );
291        }
292        if ( !result.getExceptions().isEmpty() )
293        {
294            throw new DependencyCollectionException( result );
295        }
296
297        return result;
298    }
299
300    private static RepositorySystemSession optimizeSession( RepositorySystemSession session )
301    {
302        DefaultRepositorySystemSession optimized = new DefaultRepositorySystemSession( session );
303        optimized.setArtifactTypeRegistry( CachingArtifactTypeRegistry.newInstance( session ) );
304        return optimized;
305    }
306
307    private List<Dependency> mergeDeps( List<Dependency> dominant, List<Dependency> recessive )
308    {
309        List<Dependency> result;
310        if ( dominant == null || dominant.isEmpty() )
311        {
312            result = recessive;
313        }
314        else if ( recessive == null || recessive.isEmpty() )
315        {
316            result = dominant;
317        }
318        else
319        {
320            int initialCapacity = dominant.size() + recessive.size();
321            result = new ArrayList<>( initialCapacity );
322            Collection<String> ids = new HashSet<>( initialCapacity, 1.0f );
323            for ( Dependency dependency : dominant )
324            {
325                ids.add( getId( dependency.getArtifact() ) );
326                result.add( dependency );
327            }
328            for ( Dependency dependency : recessive )
329            {
330                if ( !ids.contains( getId( dependency.getArtifact() ) ) )
331                {
332                    result.add( dependency );
333                }
334            }
335        }
336        return result;
337    }
338
339    private static String getId( Artifact a )
340    {
341        return a.getGroupId() + ':' + a.getArtifactId() + ':' + a.getClassifier() + ':' + a.getExtension();
342    }
343
344    @SuppressWarnings( "checkstyle:parameternumber" )
345    private void process( final Args args, Results results, List<Dependency> dependencies,
346                          List<RemoteRepository> repositories, DependencySelector depSelector,
347                          DependencyManager depManager, DependencyTraverser depTraverser, VersionFilter verFilter )
348    {
349        for ( Dependency dependency : dependencies )
350        {
351            processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter,
352                               dependency );
353        }
354    }
355
356    @SuppressWarnings( "checkstyle:parameternumber" )
357    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
358                                    DependencySelector depSelector, DependencyManager depManager,
359                                    DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency )
360    {
361
362        List<Artifact> relocations = Collections.emptyList();
363        boolean disableVersionManagement = false;
364        processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, dependency,
365                           relocations, disableVersionManagement );
366    }
367
368    @SuppressWarnings( "checkstyle:parameternumber" )
369    private void processDependency( Args args, Results results, List<RemoteRepository> repositories,
370                                    DependencySelector depSelector, DependencyManager depManager,
371                                    DependencyTraverser depTraverser, VersionFilter verFilter, Dependency dependency,
372                                    List<Artifact> relocations, boolean disableVersionManagement )
373    {
374
375        if ( depSelector != null && !depSelector.selectDependency( dependency ) )
376        {
377            return;
378        }
379
380        PremanagedDependency preManaged =
381            PremanagedDependency.create( depManager, dependency, disableVersionManagement, args.premanagedState );
382        dependency = preManaged.managedDependency;
383
384        boolean noDescriptor = isLackingDescriptor( dependency.getArtifact() );
385
386        boolean traverse = !noDescriptor && ( depTraverser == null || depTraverser.traverseDependency( dependency ) );
387
388        List<? extends Version> versions;
389        VersionRangeResult rangeResult;
390        try
391        {
392            VersionRangeRequest rangeRequest = createVersionRangeRequest( args, repositories, dependency );
393
394            rangeResult = cachedResolveRangeResult( rangeRequest, args.pool, args.session );
395
396            versions = filterVersions( dependency, rangeResult, verFilter, args.versionContext );
397        }
398        catch ( VersionRangeResolutionException e )
399        {
400            results.addException( dependency, e, args.nodes );
401            return;
402        }
403
404        for ( Version version : versions )
405        {
406            Artifact originalArtifact = dependency.getArtifact().setVersion( version.toString() );
407            Dependency d = dependency.setArtifact( originalArtifact );
408
409            ArtifactDescriptorRequest descriptorRequest = createArtifactDescriptorRequest( args, repositories, d );
410
411            final ArtifactDescriptorResult descriptorResult =
412                getArtifactDescriptorResult( args, results, noDescriptor, d, descriptorRequest );
413            if ( descriptorResult != null )
414            {
415                d = d.setArtifact( descriptorResult.getArtifact() );
416
417                DependencyNode node = args.nodes.top();
418
419                int cycleEntry = args.nodes.find( d.getArtifact() );
420                if ( cycleEntry >= 0 )
421                {
422                    results.addCycle( args.nodes, cycleEntry, d );
423                    DependencyNode cycleNode = args.nodes.get( cycleEntry );
424                    if ( cycleNode.getDependency() != null )
425                    {
426                        DefaultDependencyNode child =
427                            createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult,
428                                                  cycleNode );
429                        node.getChildren().add( child );
430                        continue;
431                    }
432                }
433
434                if ( !descriptorResult.getRelocations().isEmpty() )
435                {
436                    boolean disableVersionManagementSubsequently =
437                        originalArtifact.getGroupId().equals( d.getArtifact().getGroupId() )
438                            && originalArtifact.getArtifactId().equals( d.getArtifact().getArtifactId() );
439
440                    processDependency( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
441                                       descriptorResult.getRelocations(), disableVersionManagementSubsequently );
442                    return;
443                }
444                else
445                {
446                    d = args.pool.intern( d.setArtifact( args.pool.intern( d.getArtifact() ) ) );
447
448                    List<RemoteRepository> repos =
449                        getRemoteRepositories( rangeResult.getRepository( version ), repositories );
450
451                    DefaultDependencyNode child =
452                        createDependencyNode( relocations, preManaged, rangeResult, version, d,
453                                              descriptorResult.getAliases(), repos, args.request.getRequestContext() );
454
455                    node.getChildren().add( child );
456
457                    boolean recurse = traverse && !descriptorResult.getDependencies().isEmpty();
458                    if ( recurse )
459                    {
460                        doRecurse( args, results, repositories, depSelector, depManager, depTraverser, verFilter, d,
461                                   descriptorResult, child );
462                    }
463                }
464            }
465            else
466            {
467                DependencyNode node = args.nodes.top();
468                List<RemoteRepository> repos =
469                    getRemoteRepositories( rangeResult.getRepository( version ), repositories );
470                DefaultDependencyNode child =
471                    createDependencyNode( relocations, preManaged, rangeResult, version, d, null, repos,
472                                          args.request.getRequestContext() );
473                node.getChildren().add( child );
474            }
475        }
476    }
477
478    @SuppressWarnings( "checkstyle:parameternumber" )
479    private void doRecurse( Args args, Results results, List<RemoteRepository> repositories,
480                            DependencySelector depSelector, DependencyManager depManager,
481                            DependencyTraverser depTraverser, VersionFilter verFilter, Dependency d,
482                            ArtifactDescriptorResult descriptorResult, DefaultDependencyNode child )
483    {
484        DefaultDependencyCollectionContext context = args.collectionContext;
485        context.set( d, descriptorResult.getManagedDependencies() );
486
487        DependencySelector childSelector = depSelector != null ? depSelector.deriveChildSelector( context ) : null;
488        DependencyManager childManager = depManager != null ? depManager.deriveChildManager( context ) : null;
489        DependencyTraverser childTraverser = depTraverser != null ? depTraverser.deriveChildTraverser( context ) : null;
490        VersionFilter childFilter = verFilter != null ? verFilter.deriveChildFilter( context ) : null;
491
492        final List<RemoteRepository> childRepos =
493            args.ignoreRepos
494                ? repositories
495                : remoteRepositoryManager.aggregateRepositories( args.session, repositories,
496                                                                 descriptorResult.getRepositories(), true );
497
498        Object key =
499            args.pool.toKey( d.getArtifact(), childRepos, childSelector, childManager, childTraverser, childFilter );
500
501        List<DependencyNode> children = args.pool.getChildren( key );
502        if ( children == null )
503        {
504            args.pool.putChildren( key, child.getChildren() );
505
506            args.nodes.push( child );
507
508            process( args, results, descriptorResult.getDependencies(), childRepos, childSelector, childManager,
509                     childTraverser, childFilter );
510
511            args.nodes.pop();
512        }
513        else
514        {
515            child.setChildren( children );
516        }
517    }
518
519    private ArtifactDescriptorResult getArtifactDescriptorResult( Args args, Results results, boolean noDescriptor,
520                                                                  Dependency d,
521                                                                  ArtifactDescriptorRequest descriptorRequest )
522    {
523        return noDescriptor
524                   ? new ArtifactDescriptorResult( descriptorRequest )
525                   : resolveCachedArtifactDescriptor( args.pool, descriptorRequest, args.session, d, results, args );
526
527    }
528
529    private ArtifactDescriptorResult resolveCachedArtifactDescriptor( DataPool pool,
530                                                                      ArtifactDescriptorRequest descriptorRequest,
531                                                                      RepositorySystemSession session, Dependency d,
532                                                                      Results results, Args args )
533    {
534        Object key = pool.toKey( descriptorRequest );
535        ArtifactDescriptorResult descriptorResult = pool.getDescriptor( key, descriptorRequest );
536        if ( descriptorResult == null )
537        {
538            try
539            {
540                descriptorResult = descriptorReader.readArtifactDescriptor( session, descriptorRequest );
541                pool.putDescriptor( key, descriptorResult );
542            }
543            catch ( ArtifactDescriptorException e )
544            {
545                results.addException( d, e, args.nodes );
546                pool.putDescriptor( key, e );
547                return null;
548            }
549
550        }
551        else if ( descriptorResult == DataPool.NO_DESCRIPTOR )
552        {
553            return null;
554        }
555
556        return descriptorResult;
557    }
558
559    @SuppressWarnings( "checkstyle:parameternumber" )
560    private static DefaultDependencyNode createDependencyNode( List<Artifact> relocations,
561                                                               PremanagedDependency preManaged,
562                                                               VersionRangeResult rangeResult, Version version,
563                                                               Dependency d, Collection<Artifact> aliases,
564                                                               List<RemoteRepository> repos, String requestContext )
565    {
566        DefaultDependencyNode child = new DefaultDependencyNode( d );
567        preManaged.applyTo( child );
568        child.setRelocations( relocations );
569        child.setVersionConstraint( rangeResult.getVersionConstraint() );
570        child.setVersion( version );
571        child.setAliases( aliases );
572        child.setRepositories( repos );
573        child.setRequestContext( requestContext );
574        return child;
575    }
576
577    private static DefaultDependencyNode createDependencyNode( List<Artifact> relocations,
578                                                               PremanagedDependency preManaged,
579                                                               VersionRangeResult rangeResult, Version version,
580                                                               Dependency d, ArtifactDescriptorResult descriptorResult,
581                                                               DependencyNode cycleNode )
582    {
583        DefaultDependencyNode child =
584            createDependencyNode( relocations, preManaged, rangeResult, version, d, descriptorResult.getAliases(),
585                                  cycleNode.getRepositories(), cycleNode.getRequestContext() );
586        child.setChildren( cycleNode.getChildren() );
587        return child;
588    }
589
590    private static ArtifactDescriptorRequest createArtifactDescriptorRequest( Args args,
591                                                                              List<RemoteRepository> repositories,
592                                                                              Dependency d )
593    {
594        ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
595        descriptorRequest.setArtifact( d.getArtifact() );
596        descriptorRequest.setRepositories( repositories );
597        descriptorRequest.setRequestContext( args.request.getRequestContext() );
598        descriptorRequest.setTrace( args.trace );
599        return descriptorRequest;
600    }
601
602    private static VersionRangeRequest createVersionRangeRequest( Args args, List<RemoteRepository> repositories,
603                                                                  Dependency dependency )
604    {
605        VersionRangeRequest rangeRequest = new VersionRangeRequest();
606        rangeRequest.setArtifact( dependency.getArtifact() );
607        rangeRequest.setRepositories( repositories );
608        rangeRequest.setRequestContext( args.request.getRequestContext() );
609        rangeRequest.setTrace( args.trace );
610        return rangeRequest;
611    }
612
613    private VersionRangeResult cachedResolveRangeResult( VersionRangeRequest rangeRequest, DataPool pool,
614                                                         RepositorySystemSession session )
615        throws VersionRangeResolutionException
616    {
617        Object key = pool.toKey( rangeRequest );
618        VersionRangeResult rangeResult = pool.getConstraint( key, rangeRequest );
619        if ( rangeResult == null )
620        {
621            rangeResult = versionRangeResolver.resolveVersionRange( session, rangeRequest );
622            pool.putConstraint( key, rangeResult );
623        }
624        return rangeResult;
625    }
626
627    private static boolean isLackingDescriptor( Artifact artifact )
628    {
629        return artifact.getProperty( ArtifactProperties.LOCAL_PATH, null ) != null;
630    }
631
632    private static List<RemoteRepository> getRemoteRepositories( ArtifactRepository repository,
633                                                                 List<RemoteRepository> repositories )
634    {
635        if ( repository instanceof RemoteRepository )
636        {
637            return Collections.singletonList( (RemoteRepository) repository );
638        }
639        if ( repository != null )
640        {
641            return Collections.emptyList();
642        }
643        return repositories;
644    }
645
646    private static List<? extends Version> filterVersions( Dependency dependency, VersionRangeResult rangeResult,
647                                                           VersionFilter verFilter,
648                                                           DefaultVersionFilterContext verContext )
649        throws VersionRangeResolutionException
650    {
651        if ( rangeResult.getVersions().isEmpty() )
652        {
653            throw new VersionRangeResolutionException( rangeResult,
654                                                       "No versions available for " + dependency.getArtifact()
655                                                           + " within specified range" );
656        }
657
658        List<? extends Version> versions;
659        if ( verFilter != null && rangeResult.getVersionConstraint().getRange() != null )
660        {
661            verContext.set( dependency, rangeResult );
662            try
663            {
664                verFilter.filterVersions( verContext );
665            }
666            catch ( RepositoryException e )
667            {
668                throw new VersionRangeResolutionException( rangeResult,
669                                                           "Failed to filter versions for " + dependency.getArtifact()
670                                                               + ": " + e.getMessage(), e );
671            }
672            versions = verContext.get();
673            if ( versions.isEmpty() )
674            {
675                throw new VersionRangeResolutionException( rangeResult,
676                                                           "No acceptable versions for " + dependency.getArtifact()
677                                                               + ": " + rangeResult.getVersions() );
678            }
679        }
680        else
681        {
682            versions = rangeResult.getVersions();
683        }
684        return versions;
685    }
686
687    static class Args
688    {
689
690        final RepositorySystemSession session;
691
692        final boolean ignoreRepos;
693
694        final boolean premanagedState;
695
696        final RequestTrace trace;
697
698        final DataPool pool;
699
700        final NodeStack nodes;
701
702        final DefaultDependencyCollectionContext collectionContext;
703
704        final DefaultVersionFilterContext versionContext;
705
706        final CollectRequest request;
707
708        Args( RepositorySystemSession session, RequestTrace trace, DataPool pool, NodeStack nodes,
709                     DefaultDependencyCollectionContext collectionContext, DefaultVersionFilterContext versionContext,
710                     CollectRequest request )
711        {
712            this.session = session;
713            this.request = request;
714            this.ignoreRepos = session.isIgnoreArtifactDescriptorRepositories();
715            this.premanagedState = ConfigUtils.getBoolean( session, false, DependencyManagerUtils.CONFIG_PROP_VERBOSE );
716            this.trace = trace;
717            this.pool = pool;
718            this.nodes = nodes;
719            this.collectionContext = collectionContext;
720            this.versionContext = versionContext;
721        }
722
723    }
724
725    static class Results
726    {
727
728        private final CollectResult result;
729
730        final int maxExceptions;
731
732        final int maxCycles;
733
734        String errorPath;
735
736        Results( CollectResult result, RepositorySystemSession session )
737        {
738            this.result = result;
739
740            maxExceptions =
741                    ConfigUtils.getInteger( session, CONFIG_PROP_MAX_EXCEPTIONS_DEFAULT, CONFIG_PROP_MAX_EXCEPTIONS );
742
743            maxCycles = ConfigUtils.getInteger( session, CONFIG_PROP_MAX_CYCLES_DEFAULT, CONFIG_PROP_MAX_CYCLES );
744        }
745
746        public void addException( Dependency dependency, Exception e, NodeStack nodes )
747        {
748            if ( maxExceptions < 0 || result.getExceptions().size() < maxExceptions )
749            {
750                result.addException( e );
751                if ( errorPath == null )
752                {
753                    StringBuilder buffer = new StringBuilder( 256 );
754                    for ( int i = 0; i < nodes.size(); i++ )
755                    {
756                        if ( buffer.length() > 0 )
757                        {
758                            buffer.append( " -> " );
759                        }
760                        Dependency dep = nodes.get( i ).getDependency();
761                        if ( dep != null )
762                        {
763                            buffer.append( dep.getArtifact() );
764                        }
765                    }
766                    if ( buffer.length() > 0 )
767                    {
768                        buffer.append( " -> " );
769                    }
770                    buffer.append( dependency.getArtifact() );
771                    errorPath = buffer.toString();
772                }
773            }
774        }
775
776        public void addCycle( NodeStack nodes, int cycleEntry, Dependency dependency )
777        {
778            if ( maxCycles < 0 || result.getCycles().size() < maxCycles )
779            {
780                result.addCycle( new DefaultDependencyCycle( nodes, cycleEntry, dependency ) );
781            }
782        }
783
784    }
785
786    static class PremanagedDependency
787    {
788
789        final String premanagedVersion;
790
791        final String premanagedScope;
792
793        final Boolean premanagedOptional;
794
795        /**
796         * @since 1.1.0
797         */
798        final Collection<Exclusion> premanagedExclusions;
799
800        /**
801         * @since 1.1.0
802         */
803        final Map<String, String> premanagedProperties;
804
805        final int managedBits;
806
807        final Dependency managedDependency;
808
809        final boolean premanagedState;
810
811        @SuppressWarnings( "checkstyle:parameternumber" )
812        PremanagedDependency( String premanagedVersion, String premanagedScope, Boolean premanagedOptional,
813                              Collection<Exclusion> premanagedExclusions, Map<String, String> premanagedProperties,
814                              int managedBits, Dependency managedDependency, boolean premanagedState )
815        {
816            this.premanagedVersion = premanagedVersion;
817            this.premanagedScope = premanagedScope;
818            this.premanagedOptional = premanagedOptional;
819            this.premanagedExclusions =
820                premanagedExclusions != null
821                    ? Collections.unmodifiableCollection( new ArrayList<>( premanagedExclusions ) )
822                    : null;
823
824            this.premanagedProperties =
825                premanagedProperties != null
826                    ? Collections.unmodifiableMap( new HashMap<>( premanagedProperties ) )
827                    : null;
828
829            this.managedBits = managedBits;
830            this.managedDependency = managedDependency;
831            this.premanagedState = premanagedState;
832        }
833
834        static PremanagedDependency create( DependencyManager depManager, Dependency dependency,
835                                            boolean disableVersionManagement, boolean premanagedState )
836        {
837            DependencyManagement depMngt = depManager != null ? depManager.manageDependency( dependency ) : null;
838
839            int managedBits = 0;
840            String premanagedVersion = null;
841            String premanagedScope = null;
842            Boolean premanagedOptional = null;
843            Collection<Exclusion> premanagedExclusions = null;
844            Map<String, String> premanagedProperties = null;
845
846            if ( depMngt != null )
847            {
848                if ( depMngt.getVersion() != null && !disableVersionManagement )
849                {
850                    Artifact artifact = dependency.getArtifact();
851                    premanagedVersion = artifact.getVersion();
852                    dependency = dependency.setArtifact( artifact.setVersion( depMngt.getVersion() ) );
853                    managedBits |= DependencyNode.MANAGED_VERSION;
854                }
855                if ( depMngt.getProperties() != null )
856                {
857                    Artifact artifact = dependency.getArtifact();
858                    premanagedProperties = artifact.getProperties();
859                    dependency = dependency.setArtifact( artifact.setProperties( depMngt.getProperties() ) );
860                    managedBits |= DependencyNode.MANAGED_PROPERTIES;
861                }
862                if ( depMngt.getScope() != null )
863                {
864                    premanagedScope = dependency.getScope();
865                    dependency = dependency.setScope( depMngt.getScope() );
866                    managedBits |= DependencyNode.MANAGED_SCOPE;
867                }
868                if ( depMngt.getOptional() != null )
869                {
870                    premanagedOptional = dependency.isOptional();
871                    dependency = dependency.setOptional( depMngt.getOptional() );
872                    managedBits |= DependencyNode.MANAGED_OPTIONAL;
873                }
874                if ( depMngt.getExclusions() != null )
875                {
876                    premanagedExclusions = dependency.getExclusions();
877                    dependency = dependency.setExclusions( depMngt.getExclusions() );
878                    managedBits |= DependencyNode.MANAGED_EXCLUSIONS;
879                }
880            }
881            return new PremanagedDependency( premanagedVersion, premanagedScope, premanagedOptional,
882                                             premanagedExclusions, premanagedProperties, managedBits, dependency,
883                                             premanagedState );
884
885        }
886
887        public void applyTo( DefaultDependencyNode child )
888        {
889            child.setManagedBits( managedBits );
890            if ( premanagedState )
891            {
892                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_VERSION, premanagedVersion );
893                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_SCOPE, premanagedScope );
894                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_OPTIONAL, premanagedOptional );
895                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_EXCLUSIONS, premanagedExclusions );
896                child.setData( DependencyManagerUtils.NODE_DATA_PREMANAGED_PROPERTIES, premanagedProperties );
897            }
898        }
899
900    }
901
902}