001package org.apache.maven.repository.legacy.resolver;
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.Collections;
024import java.util.Iterator;
025import java.util.LinkedHashMap;
026import java.util.LinkedHashSet;
027import java.util.List;
028import java.util.Map;
029import java.util.Set;
030
031import org.apache.maven.artifact.Artifact;
032import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
033import org.apache.maven.artifact.metadata.ResolutionGroup;
034import org.apache.maven.artifact.repository.ArtifactRepository;
035import org.apache.maven.artifact.resolver.ArtifactResolutionException;
036import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
037import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
038import org.apache.maven.artifact.resolver.CyclicDependencyException;
039import org.apache.maven.artifact.resolver.ResolutionListener;
040import org.apache.maven.artifact.resolver.ResolutionListenerForDepMgmt;
041import org.apache.maven.artifact.resolver.ResolutionNode;
042import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
043import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
044import org.apache.maven.artifact.versioning.ArtifactVersion;
045import org.apache.maven.artifact.versioning.ManagedVersionMap;
046import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
047import org.apache.maven.artifact.versioning.VersionRange;
048import org.apache.maven.execution.MavenSession;
049import org.apache.maven.plugin.LegacySupport;
050import org.apache.maven.repository.legacy.metadata.ArtifactMetadataRetrievalException;
051import org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest;
052import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
053import org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver;
054import org.codehaus.plexus.component.annotations.Component;
055import org.codehaus.plexus.component.annotations.Requirement;
056import org.codehaus.plexus.logging.Logger;
057
058/**
059 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
060 * @author Jason van Zyl
061 */
062@Component( role = LegacyArtifactCollector.class )
063public class DefaultLegacyArtifactCollector
064    implements LegacyArtifactCollector
065{
066    @Requirement( hint = "nearest" )
067    private ConflictResolver defaultConflictResolver;
068
069    @Requirement
070    private Logger logger;
071
072    @Requirement
073    private LegacySupport legacySupport;
074
075    private void injectSession( ArtifactResolutionRequest request )
076    {
077        MavenSession session = legacySupport.getSession();
078
079        if ( session != null )
080        {
081            request.setOffline( session.isOffline() );
082            request.setForceUpdate( session.getRequest().isUpdateSnapshots() );
083            request.setServers( session.getRequest().getServers() );
084            request.setMirrors( session.getRequest().getMirrors() );
085            request.setProxies( session.getRequest().getProxies() );
086        }
087    }
088
089    public ArtifactResolutionResult collect( Set<Artifact> artifacts, Artifact originatingArtifact,
090                                             Map managedVersions, ArtifactRepository localRepository,
091                                             List<ArtifactRepository> remoteRepositories,
092                                             ArtifactMetadataSource source, ArtifactFilter filter,
093                                             List<ResolutionListener> listeners,
094                                             List<ConflictResolver> conflictResolvers )
095    {
096        ArtifactResolutionRequest request = new ArtifactResolutionRequest();
097        request.setLocalRepository( localRepository );
098        request.setRemoteRepositories( remoteRepositories );
099        injectSession( request );
100        return collect( artifacts, originatingArtifact, managedVersions, request, source, filter, listeners,
101                        conflictResolvers );
102    }
103
104    public ArtifactResolutionResult collect( Set<Artifact> artifacts, Artifact originatingArtifact,
105                                             Map managedVersions, ArtifactResolutionRequest repositoryRequest,
106                                             ArtifactMetadataSource source, ArtifactFilter filter,
107                                             List<ResolutionListener> listeners,
108                                             List<ConflictResolver> conflictResolvers )
109    {
110        ArtifactResolutionResult result = new ArtifactResolutionResult();
111
112        result.setOriginatingArtifact( originatingArtifact );
113
114        if ( conflictResolvers == null )
115        {
116            conflictResolvers = Collections.singletonList( defaultConflictResolver );
117        }
118
119        Map<Object, List<ResolutionNode>> resolvedArtifacts = new LinkedHashMap<Object, List<ResolutionNode>>();
120
121        ResolutionNode root = new ResolutionNode( originatingArtifact, repositoryRequest.getRemoteRepositories() );
122
123        try
124        {
125            root.addDependencies( artifacts, repositoryRequest.getRemoteRepositories(), filter );
126        }
127        catch ( CyclicDependencyException e )
128        {
129            result.addCircularDependencyException( e );
130
131            return result;
132        }
133        catch ( OverConstrainedVersionException e )
134        {
135            result.addVersionRangeViolation( e );
136
137            return result;
138        }
139
140        ManagedVersionMap versionMap = getManagedVersionsMap( originatingArtifact, managedVersions );
141
142        try
143        {
144            recurse( result, root, resolvedArtifacts, versionMap, repositoryRequest, source, filter, listeners,
145                     conflictResolvers );
146        }
147        catch ( CyclicDependencyException e )
148        {
149            logger.debug( "While recursing: " + e.getMessage(), e );
150            result.addCircularDependencyException( e );
151        }
152        catch ( OverConstrainedVersionException e )
153        {
154            logger.debug( "While recursing: " + e.getMessage(), e );
155            result.addVersionRangeViolation( e );
156        }
157        catch ( ArtifactResolutionException e )
158        {
159            logger.debug( "While recursing: " + e.getMessage(), e );
160            result.addErrorArtifactException( e );
161        }
162
163        Set<ResolutionNode> set = new LinkedHashSet<ResolutionNode>();
164
165        for ( List<ResolutionNode> nodes : resolvedArtifacts.values() )
166        {
167            for ( ResolutionNode node : nodes )
168            {
169                if ( !node.equals( root ) && node.isActive() )
170                {
171                    Artifact artifact = node.getArtifact();
172
173                    try
174                    {
175                        if ( node.filterTrail( filter ) )
176                        {
177                            // If it was optional and not a direct dependency,
178                            // we don't add it or its children, just allow the update of the version and artifactScope
179                            if ( node.isChildOfRootNode() || !artifact.isOptional() )
180                            {
181                                artifact.setDependencyTrail( node.getDependencyTrail() );
182
183                                set.add( node );
184
185                                // This is required right now.
186                                result.addArtifact( artifact );
187                            }
188                        }
189                    }
190                    catch ( OverConstrainedVersionException e )
191                    {
192                        result.addVersionRangeViolation( e );
193                    }
194                }
195            }
196        }
197
198        result.setArtifactResolutionNodes( set );
199
200        return result;
201    }
202
203    /**
204     * Get the map of managed versions, removing the originating artifact if it is also in managed versions
205     *
206     * @param originatingArtifact artifact we are processing
207     * @param managedVersions original managed versions
208     */
209    private ManagedVersionMap getManagedVersionsMap( Artifact originatingArtifact, Map managedVersions )
210    {
211        ManagedVersionMap versionMap;
212        if ( ( managedVersions != null ) && ( managedVersions instanceof ManagedVersionMap ) )
213        {
214            versionMap = (ManagedVersionMap) managedVersions;
215        }
216        else
217        {
218            versionMap = new ManagedVersionMap( managedVersions );
219        }
220
221        /* remove the originating artifact if it is also in managed versions to avoid being modified during resolution */
222        Artifact managedOriginatingArtifact = versionMap.get( originatingArtifact.getDependencyConflictId() );
223
224        if ( managedOriginatingArtifact != null )
225        {
226            // TODO we probably want to warn the user that he is building an artifact with
227            // different values than in dependencyManagement
228            if ( managedVersions instanceof ManagedVersionMap )
229            {
230                /* avoid modifying the managedVersions parameter creating a new map */
231                versionMap = new ManagedVersionMap( managedVersions );
232            }
233            versionMap.remove( originatingArtifact.getDependencyConflictId() );
234        }
235
236        return versionMap;
237    }
238
239    private void recurse( ArtifactResolutionResult result, ResolutionNode node,
240                          Map<Object, List<ResolutionNode>> resolvedArtifacts, ManagedVersionMap managedVersions,
241                          ArtifactResolutionRequest request, ArtifactMetadataSource source, ArtifactFilter filter,
242                          List<ResolutionListener> listeners, List<ConflictResolver> conflictResolvers )
243        throws ArtifactResolutionException
244    {
245        fireEvent( ResolutionListener.TEST_ARTIFACT, listeners, node );
246
247        Object key = node.getKey();
248
249        // TODO: Does this check need to happen here? Had to add the same call
250        // below when we iterate on child nodes -- will that suffice?
251        if ( managedVersions.containsKey( key ) )
252        {
253            manageArtifact( node, managedVersions, listeners );
254        }
255
256        List<ResolutionNode> previousNodes = resolvedArtifacts.get( key );
257
258        if ( previousNodes != null )
259        {
260            for ( ResolutionNode previous : previousNodes )
261            {
262                try
263                {
264                    if ( previous.isActive() )
265                    {
266                        // Version mediation
267                        VersionRange previousRange = previous.getArtifact().getVersionRange();
268                        VersionRange currentRange = node.getArtifact().getVersionRange();
269
270                        if ( ( previousRange != null ) && ( currentRange != null ) )
271                        {
272                            // TODO: shouldn't need to double up on this work, only done for simplicity of handling
273                            // recommended
274                            // version but the restriction is identical
275                            VersionRange newRange = previousRange.restrict( currentRange );
276                            // TODO: ick. this forces the OCE that should have come from the previous call. It is still
277                            // correct
278                            if ( newRange.isSelectedVersionKnown( previous.getArtifact() ) )
279                            {
280                                fireEvent( ResolutionListener.RESTRICT_RANGE, listeners, node, previous.getArtifact(),
281                                           newRange );
282                            }
283                            previous.getArtifact().setVersionRange( newRange );
284                            node.getArtifact().setVersionRange( currentRange.restrict( previousRange ) );
285
286                            // Select an appropriate available version from the (now restricted) range
287                            // Note this version was selected before to get the appropriate POM
288                            // But it was reset by the call to setVersionRange on restricting the version
289                            ResolutionNode[] resetNodes = { previous, node };
290                            for ( int j = 0; j < 2; j++ )
291                            {
292                                Artifact resetArtifact = resetNodes[j].getArtifact();
293
294                                // MNG-2123: if the previous node was not a range, then it wouldn't have any available
295                                // versions. We just clobbered the selected version above. (why? i have no idea.)
296                                // So since we are here and this is ranges we must go figure out the version (for a
297                                // third time...)
298                                if ( resetArtifact.getVersion() == null && resetArtifact.getVersionRange() != null )
299                                {
300
301                                    // go find the version. This is a total hack. See previous comment.
302                                    List<ArtifactVersion> versions = resetArtifact.getAvailableVersions();
303                                    if ( versions == null )
304                                    {
305                                        try
306                                        {
307                                            MetadataResolutionRequest metadataRequest =
308                                                new DefaultMetadataResolutionRequest( request );
309                                            metadataRequest.setArtifact( resetArtifact );
310                                            versions = source.retrieveAvailableVersions( metadataRequest );
311                                            resetArtifact.setAvailableVersions( versions );
312                                        }
313                                        catch ( ArtifactMetadataRetrievalException e )
314                                        {
315                                            resetArtifact.setDependencyTrail( node.getDependencyTrail() );
316                                            throw new ArtifactResolutionException(
317                                                                                   "Unable to get dependency information: "
318                                                                                       + e.getMessage(), resetArtifact,
319                                                                                   request.getRemoteRepositories(), e );
320                                        }
321                                    }
322                                    // end hack
323
324                                    // MNG-2861: match version can return null
325                                    ArtifactVersion selectedVersion =
326                                        resetArtifact.getVersionRange().matchVersion( resetArtifact.getAvailableVersions() );
327                                    if ( selectedVersion != null )
328                                    {
329                                        resetArtifact.selectVersion( selectedVersion.toString() );
330                                    }
331                                    else
332                                    {
333                                        throw new OverConstrainedVersionException( " Unable to find a version in "
334                                            + resetArtifact.getAvailableVersions() + " to match the range "
335                                            + resetArtifact.getVersionRange(), resetArtifact );
336                                    }
337
338                                    fireEvent( ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, resetNodes[j] );
339                                }
340                            }
341                        }
342
343                        // Conflict Resolution
344                        ResolutionNode resolved = null;
345                        for ( Iterator j = conflictResolvers.iterator(); ( resolved == null ) && j.hasNext(); )
346                        {
347                            ConflictResolver conflictResolver = (ConflictResolver) j.next();
348
349                            resolved = conflictResolver.resolveConflict( previous, node );
350                        }
351
352                        if ( resolved == null )
353                        {
354                            // TODO: add better exception that can detail the two conflicting artifacts
355                            ArtifactResolutionException are =
356                                new ArtifactResolutionException( "Cannot resolve artifact version conflict between "
357                                    + previous.getArtifact().getVersion() + " and " + node.getArtifact().getVersion(),
358                                                                 previous.getArtifact() );
359                            result.addVersionRangeViolation( are );
360                        }
361
362                        if ( ( resolved != previous ) && ( resolved != node ) )
363                        {
364                            // TODO: add better exception
365                            result.addVersionRangeViolation( new ArtifactResolutionException(
366                                                                                              "Conflict resolver returned unknown resolution node: ",
367                                                                                              resolved.getArtifact() ) );
368                        }
369
370                        // TODO: should this be part of mediation?
371                        // previous one is more dominant
372                        ResolutionNode nearest;
373                        ResolutionNode farthest;
374
375                        if ( resolved == previous )
376                        {
377                            nearest = previous;
378                            farthest = node;
379                        }
380                        else
381                        {
382                            nearest = node;
383                            farthest = previous;
384                        }
385
386                        if ( checkScopeUpdate( farthest, nearest, listeners ) )
387                        {
388                            // if we need to update artifactScope of nearest to use farthest artifactScope, use the
389                            // nearest version, but farthest artifactScope
390                            nearest.disable();
391                            farthest.getArtifact().setVersion( nearest.getArtifact().getVersion() );
392                            fireEvent( ResolutionListener.OMIT_FOR_NEARER, listeners, nearest, farthest.getArtifact() );
393                        }
394                        else
395                        {
396                            farthest.disable();
397                            fireEvent( ResolutionListener.OMIT_FOR_NEARER, listeners, farthest, nearest.getArtifact() );
398                        }
399                    }
400                }
401                catch ( OverConstrainedVersionException e )
402                {
403                    result.addVersionRangeViolation( e );
404                }
405            }
406        }
407        else
408        {
409            previousNodes = new ArrayList<ResolutionNode>();
410
411            resolvedArtifacts.put( key, previousNodes );
412        }
413        previousNodes.add( node );
414
415        if ( node.isActive() )
416        {
417            fireEvent( ResolutionListener.INCLUDE_ARTIFACT, listeners, node );
418        }
419
420        // don't pull in the transitive deps of a system-scoped dependency.
421        if ( node.isActive() && !Artifact.SCOPE_SYSTEM.equals( node.getArtifact().getScope() ) )
422        {
423            fireEvent( ResolutionListener.PROCESS_CHILDREN, listeners, node );
424
425            Artifact parentArtifact = node.getArtifact();
426
427            for ( Iterator i = node.getChildrenIterator(); i.hasNext(); )
428            {
429                ResolutionNode child = (ResolutionNode) i.next();
430
431                try
432                {
433
434                    // We leave in optional ones, but don't pick up its dependencies
435                    if ( !child.isResolved() && ( !child.getArtifact().isOptional() || child.isChildOfRootNode() ) )
436                    {
437                        Artifact artifact = child.getArtifact();
438                        artifact.setDependencyTrail( node.getDependencyTrail() );
439                        List<ArtifactRepository> childRemoteRepositories = child.getRemoteRepositories();
440
441                        MetadataResolutionRequest metadataRequest =
442                            new DefaultMetadataResolutionRequest( request );
443                        metadataRequest.setArtifact( artifact );
444                        metadataRequest.setRemoteRepositories( childRemoteRepositories );
445
446                        try
447                        {
448                            ResolutionGroup rGroup;
449
450                            Object childKey;
451                            do
452                            {
453                                childKey = child.getKey();
454
455                                if ( managedVersions.containsKey( childKey ) )
456                                {
457                                    // If this child node is a managed dependency, ensure
458                                    // we are using the dependency management version
459                                    // of this child if applicable b/c we want to use the
460                                    // managed version's POM, *not* any other version's POM.
461                                    // We retrieve the POM below in the retrieval step.
462                                    manageArtifact( child, managedVersions, listeners );
463
464                                    // Also, we need to ensure that any exclusions it presents are
465                                    // added to the artifact before we retrive the metadata
466                                    // for the artifact; otherwise we may end up with unwanted
467                                    // dependencies.
468                                    Artifact ma = managedVersions.get( childKey );
469                                    ArtifactFilter managedExclusionFilter = ma.getDependencyFilter();
470                                    if ( null != managedExclusionFilter )
471                                    {
472                                        if ( null != artifact.getDependencyFilter() )
473                                        {
474                                            AndArtifactFilter aaf = new AndArtifactFilter();
475                                            aaf.add( artifact.getDependencyFilter() );
476                                            aaf.add( managedExclusionFilter );
477                                            artifact.setDependencyFilter( aaf );
478                                        }
479                                        else
480                                        {
481                                            artifact.setDependencyFilter( managedExclusionFilter );
482                                        }
483                                    }
484                                }
485
486                                if ( artifact.getVersion() == null )
487                                {
488                                    // set the recommended version
489                                    // TODO: maybe its better to just pass the range through to retrieval and use a
490                                    // transformation?
491                                    ArtifactVersion version;
492                                    if ( !artifact.isSelectedVersionKnown() )
493                                    {
494                                        List<ArtifactVersion> versions = artifact.getAvailableVersions();
495                                        if ( versions == null )
496                                        {
497                                            versions = source.retrieveAvailableVersions( metadataRequest );
498                                            artifact.setAvailableVersions( versions );
499                                        }
500
501                                        Collections.sort( versions );
502
503                                        VersionRange versionRange = artifact.getVersionRange();
504
505                                        version = versionRange.matchVersion( versions );
506
507                                        if ( version == null )
508                                        {
509                                            if ( versions.isEmpty() )
510                                            {
511                                                throw new OverConstrainedVersionException(
512                                                                                           "No versions are present in the repository for the artifact with a range "
513                                                                                               + versionRange,
514                                                                                           artifact,
515                                                                                           childRemoteRepositories );
516                                            }
517
518                                            throw new OverConstrainedVersionException( "Couldn't find a version in "
519                                                + versions + " to match range " + versionRange, artifact,
520                                                childRemoteRepositories );
521                                        }
522                                    }
523                                    else
524                                    {
525                                        version = artifact.getSelectedVersion();
526                                    }
527
528                                    artifact.selectVersion( version.toString() );
529                                    fireEvent( ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, child );
530                                }
531
532                                rGroup = source.retrieve( metadataRequest );
533
534                                if ( rGroup == null )
535                                {
536                                    break;
537                                }
538                            }
539                            while( !childKey.equals( child.getKey() ) );
540
541                            if ( parentArtifact != null && parentArtifact.getDependencyFilter() != null
542                                && !parentArtifact.getDependencyFilter().include( artifact ) )
543                            {
544                                // MNG-3769: the [probably relocated] artifact is excluded.
545                                // We could process exclusions on relocated artifact details in the
546                                // MavenMetadataSource.createArtifacts(..) step, BUT that would
547                                // require resolving the POM from the repository very early on in
548                                // the build.
549                                continue;
550                            }
551
552                            // TODO might be better to have source.retrieve() throw a specific exception for this
553                            // situation
554                            // and catch here rather than have it return null
555                            if ( rGroup == null )
556                            {
557                                // relocated dependency artifact is declared excluded, no need to add and recurse
558                                // further
559                                continue;
560                            }
561
562                            child.addDependencies( rGroup.getArtifacts(), rGroup.getResolutionRepositories(), filter );
563
564                        }
565                        catch ( CyclicDependencyException e )
566                        {
567                            // would like to throw this, but we have crappy stuff in the repo
568
569                            fireEvent( ResolutionListener.OMIT_FOR_CYCLE, listeners,
570                                       new ResolutionNode( e.getArtifact(), childRemoteRepositories, child ) );
571                        }
572                        catch ( ArtifactMetadataRetrievalException e )
573                        {
574                            artifact.setDependencyTrail( node.getDependencyTrail() );
575
576                            throw new ArtifactResolutionException( "Unable to get dependency information for "
577                                + artifact.getId() + ": " + e.getMessage(), artifact, childRemoteRepositories, e );
578                        }
579
580                        ArtifactResolutionRequest subRequest = new ArtifactResolutionRequest( metadataRequest );
581                        subRequest.setServers( request.getServers() );
582                        subRequest.setMirrors( request.getMirrors() );
583                        subRequest.setProxies( request.getProxies() );
584                        recurse( result, child, resolvedArtifacts, managedVersions, subRequest, source, filter,
585                                 listeners, conflictResolvers );
586                    }
587                }
588                catch ( OverConstrainedVersionException e )
589                {
590                    result.addVersionRangeViolation( e );
591                }
592                catch ( ArtifactResolutionException e )
593                {
594                    result.addMetadataResolutionException( e );
595                }
596            }
597
598            fireEvent( ResolutionListener.FINISH_PROCESSING_CHILDREN, listeners, node );
599        }
600    }
601
602    private void manageArtifact( ResolutionNode node, ManagedVersionMap managedVersions,
603                                 List<ResolutionListener> listeners )
604    {
605        Artifact artifact = managedVersions.get( node.getKey() );
606
607        // Before we update the version of the artifact, we need to know
608        // whether we are working on a transitive dependency or not. This
609        // allows depMgmt to always override transitive dependencies, while
610        // explicit child override depMgmt (viz. depMgmt should only
611        // provide defaults to children, but should override transitives).
612        // We can do this by calling isChildOfRootNode on the current node.
613
614        if ( ( artifact.getVersion() != null )
615            && ( !node.isChildOfRootNode() || node.getArtifact().getVersion() == null ) )
616        {
617            fireEvent( ResolutionListener.MANAGE_ARTIFACT_VERSION, listeners, node, artifact );
618            node.getArtifact().setVersion( artifact.getVersion() );
619        }
620
621        if ( ( artifact.getScope() != null ) && ( !node.isChildOfRootNode() || node.getArtifact().getScope() == null ) )
622        {
623            fireEvent( ResolutionListener.MANAGE_ARTIFACT_SCOPE, listeners, node, artifact );
624            node.getArtifact().setScope( artifact.getScope() );
625        }
626
627        if ( Artifact.SCOPE_SYSTEM.equals( node.getArtifact().getScope() ) && ( node.getArtifact().getFile() == null )
628            && ( artifact.getFile() != null ) )
629        {
630            fireEvent( ResolutionListener.MANAGE_ARTIFACT_SYSTEM_PATH, listeners, node, artifact );
631            node.getArtifact().setFile( artifact.getFile() );
632        }
633    }
634
635    /**
636     * Check if the artifactScope needs to be updated. <a
637     * href="http://docs.codehaus.org/x/IGU#DependencyMediationandConflictResolution-Scoperesolution">More info</a>.
638     *
639     * @param farthest farthest resolution node
640     * @param nearest nearest resolution node
641     * @param listeners
642     */
643    boolean checkScopeUpdate( ResolutionNode farthest, ResolutionNode nearest, List<ResolutionListener> listeners )
644    {
645        boolean updateScope = false;
646        Artifact farthestArtifact = farthest.getArtifact();
647        Artifact nearestArtifact = nearest.getArtifact();
648
649        /* farthest is runtime and nearest has lower priority, change to runtime */
650        if ( Artifact.SCOPE_RUNTIME.equals( farthestArtifact.getScope() )
651            && ( Artifact.SCOPE_TEST.equals( nearestArtifact.getScope() )
652                            || Artifact.SCOPE_PROVIDED.equals( nearestArtifact.getScope() ) ) )
653        {
654            updateScope = true;
655        }
656
657        /* farthest is compile and nearest is not (has lower priority), change to compile */
658        if ( Artifact.SCOPE_COMPILE.equals( farthestArtifact.getScope() )
659            && !Artifact.SCOPE_COMPILE.equals( nearestArtifact.getScope() ) )
660        {
661            updateScope = true;
662        }
663
664        /* current POM rules all, if nearest is in current pom, do not update its artifactScope */
665        if ( ( nearest.getDepth() < 2 ) && updateScope )
666        {
667            updateScope = false;
668
669            fireEvent( ResolutionListener.UPDATE_SCOPE_CURRENT_POM, listeners, nearest, farthestArtifact );
670        }
671
672        if ( updateScope )
673        {
674            fireEvent( ResolutionListener.UPDATE_SCOPE, listeners, nearest, farthestArtifact );
675
676            // previously we cloned the artifact, but it is more effecient to just update the artifactScope
677            // if problems are later discovered that the original object needs its original artifactScope value, cloning
678            // may
679            // again be appropriate
680            nearestArtifact.setScope( farthestArtifact.getScope() );
681        }
682
683        return updateScope;
684    }
685
686    private void fireEvent( int event, List<ResolutionListener> listeners, ResolutionNode node )
687    {
688        fireEvent( event, listeners, node, null );
689    }
690
691    private void fireEvent( int event, List<ResolutionListener> listeners, ResolutionNode node, Artifact replacement )
692    {
693        fireEvent( event, listeners, node, replacement, null );
694    }
695
696    private void fireEvent( int event, List<ResolutionListener> listeners, ResolutionNode node, Artifact replacement,
697                            VersionRange newRange )
698    {
699        for ( ResolutionListener listener : listeners )
700        {
701            switch ( event )
702            {
703                case ResolutionListener.TEST_ARTIFACT:
704                    listener.testArtifact( node.getArtifact() );
705                    break;
706                case ResolutionListener.PROCESS_CHILDREN:
707                    listener.startProcessChildren( node.getArtifact() );
708                    break;
709                case ResolutionListener.FINISH_PROCESSING_CHILDREN:
710                    listener.endProcessChildren( node.getArtifact() );
711                    break;
712                case ResolutionListener.INCLUDE_ARTIFACT:
713                    listener.includeArtifact( node.getArtifact() );
714                    break;
715                case ResolutionListener.OMIT_FOR_NEARER:
716                    listener.omitForNearer( node.getArtifact(), replacement );
717                    break;
718                case ResolutionListener.OMIT_FOR_CYCLE:
719                    listener.omitForCycle( node.getArtifact() );
720                    break;
721                case ResolutionListener.UPDATE_SCOPE:
722                    listener.updateScope( node.getArtifact(), replacement.getScope() );
723                    break;
724                case ResolutionListener.UPDATE_SCOPE_CURRENT_POM:
725                    listener.updateScopeCurrentPom( node.getArtifact(), replacement.getScope() );
726                    break;
727                case ResolutionListener.MANAGE_ARTIFACT_VERSION:
728                    if ( listener instanceof ResolutionListenerForDepMgmt )
729                    {
730                        ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
731                        asImpl.manageArtifactVersion( node.getArtifact(), replacement );
732                    }
733                    else
734                    {
735                        listener.manageArtifact( node.getArtifact(), replacement );
736                    }
737                    break;
738                case ResolutionListener.MANAGE_ARTIFACT_SCOPE:
739                    if ( listener instanceof ResolutionListenerForDepMgmt )
740                    {
741                        ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
742                        asImpl.manageArtifactScope( node.getArtifact(), replacement );
743                    }
744                    else
745                    {
746                        listener.manageArtifact( node.getArtifact(), replacement );
747                    }
748                    break;
749                case ResolutionListener.MANAGE_ARTIFACT_SYSTEM_PATH:
750                    if ( listener instanceof ResolutionListenerForDepMgmt )
751                    {
752                        ResolutionListenerForDepMgmt asImpl = (ResolutionListenerForDepMgmt) listener;
753                        asImpl.manageArtifactSystemPath( node.getArtifact(), replacement );
754                    }
755                    else
756                    {
757                        listener.manageArtifact( node.getArtifact(), replacement );
758                    }
759                    break;
760                case ResolutionListener.SELECT_VERSION_FROM_RANGE:
761                    listener.selectVersionFromRange( node.getArtifact() );
762                    break;
763                case ResolutionListener.RESTRICT_RANGE:
764                    if ( node.getArtifact().getVersionRange().hasRestrictions()
765                        || replacement.getVersionRange().hasRestrictions() )
766                    {
767                        listener.restrictRange( node.getArtifact(), replacement, newRange );
768                    }
769                    break;
770                default:
771                    throw new IllegalStateException( "Unknown event: " + event );
772            }
773        }
774    }
775
776    public ArtifactResolutionResult collect( Set<Artifact> artifacts, Artifact originatingArtifact,
777                                             Map managedVersions, ArtifactRepository localRepository,
778                                             List<ArtifactRepository> remoteRepositories,
779                                             ArtifactMetadataSource source, ArtifactFilter filter,
780                                             List<ResolutionListener> listeners )
781    {
782        return collect( artifacts, originatingArtifact, managedVersions, localRepository, remoteRepositories, source,
783                        filter, listeners, null );
784    }
785
786    public ArtifactResolutionResult collect( Set<Artifact> artifacts, Artifact originatingArtifact,
787                                             ArtifactRepository localRepository,
788                                             List<ArtifactRepository> remoteRepositories,
789                                             ArtifactMetadataSource source, ArtifactFilter filter,
790                                             List<ResolutionListener> listeners )
791    {
792        return collect( artifacts, originatingArtifact, null, localRepository, remoteRepositories, source, filter,
793                        listeners );
794    }
795
796}