private void recurse()

in compat/maven-compat/src/main/java/org/apache/maven/repository/legacy/resolver/DefaultLegacyArtifactCollector.java [229:563]


    private void recurse(
            ArtifactResolutionResult result,
            ResolutionNode node,
            Map<Object, List<ResolutionNode>> resolvedArtifacts,
            ManagedVersionMap managedVersions,
            ArtifactResolutionRequest request,
            ArtifactMetadataSource source,
            ArtifactFilter filter,
            List<ResolutionListener> listeners,
            List<ConflictResolver> conflictResolvers)
            throws ArtifactResolutionException {
        fireEvent(ResolutionListener.TEST_ARTIFACT, listeners, node);

        Object key = node.getKey();

        // TODO Does this check need to happen here? Had to add the same call
        // below when we iterate on child nodes -- will that suffice?
        if (managedVersions.containsKey(key)) {
            manageArtifact(node, managedVersions, listeners);
        }

        List<ResolutionNode> previousNodes = resolvedArtifacts.get(key);

        if (previousNodes != null) {
            for (ResolutionNode previous : previousNodes) {
                try {
                    if (previous.isActive()) {
                        // Version mediation
                        VersionRange previousRange = previous.getArtifact().getVersionRange();
                        VersionRange currentRange = node.getArtifact().getVersionRange();

                        if ((previousRange != null) && (currentRange != null)) {
                            // TODO shouldn't need to double up on this work, only done for simplicity of handling
                            // recommended
                            // version but the restriction is identical
                            VersionRange newRange = previousRange.restrict(currentRange);
                            // TODO ick. this forces the OCE that should have come from the previous call. It is still
                            // correct
                            if (newRange.isSelectedVersionKnown(previous.getArtifact())) {
                                fireEvent(
                                        ResolutionListener.RESTRICT_RANGE,
                                        listeners,
                                        node,
                                        previous.getArtifact(),
                                        newRange);
                            }
                            previous.getArtifact().setVersionRange(newRange);
                            node.getArtifact().setVersionRange(currentRange.restrict(previousRange));

                            // Select an appropriate available version from the (now restricted) range
                            // Note this version was selected before to get the appropriate POM
                            // But it was reset by the call to setVersionRange on restricting the version
                            ResolutionNode[] resetNodes = {previous, node};
                            for (int j = 0; j < 2; j++) {
                                Artifact resetArtifact = resetNodes[j].getArtifact();

                                // MNG-2123: if the previous node was not a range, then it wouldn't have any available
                                // versions. We just clobbered the selected version above. (why? I have no idea.)
                                // So since we are here and this is ranges we must go figure out the version (for a
                                // third time...)
                                if (resetArtifact.getVersion() == null && resetArtifact.getVersionRange() != null) {

                                    // go find the version. This is a total hack. See previous comment.
                                    List<ArtifactVersion> versions = resetArtifact.getAvailableVersions();
                                    if (versions == null) {
                                        try {
                                            MetadataResolutionRequest metadataRequest =
                                                    new DefaultMetadataResolutionRequest(request);

                                            metadataRequest.setArtifact(resetArtifact);
                                            versions = source.retrieveAvailableVersions(metadataRequest);
                                            resetArtifact.setAvailableVersions(versions);
                                        } catch (ArtifactMetadataRetrievalException e) {
                                            resetArtifact.setDependencyTrail(node.getDependencyTrail());
                                            throw new ArtifactResolutionException(
                                                    "Unable to get dependency information: " + e.getMessage(),
                                                    resetArtifact,
                                                    request.getRemoteRepositories(),
                                                    e);
                                        }
                                    }
                                    // end hack

                                    // MNG-2861: match version can return null
                                    ArtifactVersion selectedVersion = resetArtifact
                                            .getVersionRange()
                                            .matchVersion(resetArtifact.getAvailableVersions());

                                    if (selectedVersion != null) {
                                        resetArtifact.selectVersion(selectedVersion.toString());
                                    } else {
                                        throw new OverConstrainedVersionException(
                                                "Unable to find a version in " + resetArtifact.getAvailableVersions()
                                                        + " to match the range " + resetArtifact.getVersionRange(),
                                                resetArtifact);
                                    }

                                    fireEvent(ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, resetNodes[j]);
                                }
                            }
                        }

                        // Conflict Resolution
                        ResolutionNode resolved = null;
                        for (Iterator<ConflictResolver> j = conflictResolvers.iterator();
                                resolved == null && j.hasNext(); ) {
                            ConflictResolver conflictResolver = j.next();

                            resolved = conflictResolver.resolveConflict(previous, node);
                        }

                        if (resolved == null) {
                            // TODO add better exception that can detail the two conflicting artifacts
                            ArtifactResolutionException are = new ArtifactResolutionException(
                                    "Cannot resolve artifact version conflict between "
                                            + previous.getArtifact().getVersion() + " and "
                                            + node.getArtifact().getVersion(),
                                    previous.getArtifact());

                            result.addVersionRangeViolation(are);
                        }

                        if ((resolved != previous) && (resolved != node)) {
                            // TODO add better exception
                            result.addVersionRangeViolation(new ArtifactResolutionException(
                                    "Conflict resolver returned unknown resolution node: ", resolved.getArtifact()));
                        }

                        // TODO should this be part of mediation?
                        // previous one is more dominant
                        ResolutionNode nearest;
                        ResolutionNode farthest;

                        if (resolved == previous) {
                            nearest = previous;
                            farthest = node;
                        } else {
                            nearest = node;
                            farthest = previous;
                        }

                        if (checkScopeUpdate(farthest, nearest, listeners)) {
                            // if we need to update artifactScope of nearest to use farthest artifactScope, use the
                            // nearest version, but farthest artifactScope
                            nearest.disable();
                            farthest.getArtifact()
                                    .setVersion(nearest.getArtifact().getVersion());
                            fireEvent(ResolutionListener.OMIT_FOR_NEARER, listeners, nearest, farthest.getArtifact());
                        } else {
                            farthest.disable();
                            fireEvent(ResolutionListener.OMIT_FOR_NEARER, listeners, farthest, nearest.getArtifact());
                        }
                    }
                } catch (OverConstrainedVersionException e) {
                    result.addVersionRangeViolation(e);
                }
            }
        } else {
            previousNodes = new ArrayList<>();

            resolvedArtifacts.put(key, previousNodes);
        }
        previousNodes.add(node);

        if (node.isActive()) {
            fireEvent(ResolutionListener.INCLUDE_ARTIFACT, listeners, node);
        }

        // don't pull in the transitive deps of a system-scoped dependency.
        if (node.isActive() && !Artifact.SCOPE_SYSTEM.equals(node.getArtifact().getScope())) {
            fireEvent(ResolutionListener.PROCESS_CHILDREN, listeners, node);

            Artifact parentArtifact = node.getArtifact();

            for (Iterator<ResolutionNode> i = node.getChildrenIterator(); i.hasNext(); ) {
                ResolutionNode child = i.next();

                try {

                    // We leave in optional ones, but don't pick up its dependencies
                    if (!child.isResolved() && (!child.getArtifact().isOptional() || child.isChildOfRootNode())) {
                        Artifact artifact = child.getArtifact();
                        artifact.setDependencyTrail(node.getDependencyTrail());
                        List<ArtifactRepository> childRemoteRepositories = child.getRemoteRepositories();

                        MetadataResolutionRequest metadataRequest = new DefaultMetadataResolutionRequest(request);
                        metadataRequest.setArtifact(artifact);
                        metadataRequest.setRemoteRepositories(childRemoteRepositories);

                        try {
                            ResolutionGroup rGroup;

                            Object childKey;
                            do {
                                childKey = child.getKey();

                                if (managedVersions.containsKey(childKey)) {
                                    // If this child node is a managed dependency, ensure
                                    // we are using the dependency management version
                                    // of this child if applicable b/c we want to use the
                                    // managed version's POM, *not* any other version's POM.
                                    // We retrieve the POM below in the retrieval step.
                                    manageArtifact(child, managedVersions, listeners);

                                    // Also, we need to ensure that any exclusions it presents are
                                    // added to the artifact before we retrieve the metadata
                                    // for the artifact; otherwise we may end up with unwanted
                                    // dependencies.
                                    Artifact ma = managedVersions.get(childKey);
                                    ArtifactFilter managedExclusionFilter = ma.getDependencyFilter();
                                    if (null != managedExclusionFilter) {
                                        if (null != artifact.getDependencyFilter()) {
                                            AndArtifactFilter aaf = new AndArtifactFilter();
                                            aaf.add(artifact.getDependencyFilter());
                                            aaf.add(managedExclusionFilter);
                                            artifact.setDependencyFilter(aaf);
                                        } else {
                                            artifact.setDependencyFilter(managedExclusionFilter);
                                        }
                                    }
                                }

                                if (artifact.getVersion() == null) {
                                    // set the recommended version
                                    // TODO maybe its better to just pass the range through to retrieval and use a
                                    // transformation?
                                    ArtifactVersion version;
                                    if (!artifact.isSelectedVersionKnown()) {
                                        List<ArtifactVersion> versions = artifact.getAvailableVersions();
                                        if (versions == null) {
                                            versions = source.retrieveAvailableVersions(metadataRequest);
                                            artifact.setAvailableVersions(versions);
                                        }

                                        Collections.sort(versions);

                                        VersionRange versionRange = artifact.getVersionRange();

                                        version = versionRange.matchVersion(versions);

                                        if (version == null) {
                                            if (versions.isEmpty()) {
                                                throw new OverConstrainedVersionException(
                                                        "No versions are present in the repository for the artifact"
                                                                + " with a range " + versionRange,
                                                        artifact,
                                                        childRemoteRepositories);
                                            }

                                            throw new OverConstrainedVersionException(
                                                    "Couldn't find a version in " + versions + " to match range "
                                                            + versionRange,
                                                    artifact,
                                                    childRemoteRepositories);
                                        }
                                    } else {
                                        version = artifact.getSelectedVersion();
                                    }

                                    artifact.selectVersion(version.toString());
                                    fireEvent(ResolutionListener.SELECT_VERSION_FROM_RANGE, listeners, child);
                                }

                                rGroup = source.retrieve(metadataRequest);

                                if (rGroup == null) {
                                    break;
                                }
                            } while (!childKey.equals(child.getKey()));

                            if (parentArtifact != null
                                    && parentArtifact.getDependencyFilter() != null
                                    && !parentArtifact.getDependencyFilter().include(artifact)) {
                                // MNG-3769: the [probably relocated] artifact is excluded.
                                // We could process exclusions on relocated artifact details in the
                                // MavenMetadataSource.createArtifacts(..) step, BUT that would
                                // require resolving the POM from the repository very early on in
                                // the build.
                                continue;
                            }

                            // TODO might be better to have source.retrieve() throw a specific exception for this
                            // situation
                            // and catch here rather than have it return null
                            if (rGroup == null) {
                                // relocated dependency artifact is declared excluded, no need to add and recurse
                                // further
                                continue;
                            }

                            child.addDependencies(rGroup.getArtifacts(), rGroup.getResolutionRepositories(), filter);

                        } catch (CyclicDependencyException e) {
                            // would like to throw this, but we have crappy stuff in the repo

                            fireEvent(
                                    ResolutionListener.OMIT_FOR_CYCLE,
                                    listeners,
                                    new ResolutionNode(e.getArtifact(), childRemoteRepositories, child));
                        } catch (ArtifactMetadataRetrievalException e) {
                            artifact.setDependencyTrail(node.getDependencyTrail());

                            throw new ArtifactResolutionException(
                                    "Unable to get dependency information for " + artifact.getId() + ": "
                                            + e.getMessage(),
                                    artifact,
                                    childRemoteRepositories,
                                    e);
                        }

                        ArtifactResolutionRequest subRequest = new ArtifactResolutionRequest(metadataRequest);
                        subRequest.setServers(request.getServers());
                        subRequest.setMirrors(request.getMirrors());
                        subRequest.setProxies(request.getProxies());
                        recurse(
                                result,
                                child,
                                resolvedArtifacts,
                                managedVersions,
                                subRequest,
                                source,
                                filter,
                                listeners,
                                conflictResolvers);
                    }
                } catch (OverConstrainedVersionException e) {
                    result.addVersionRangeViolation(e);
                } catch (ArtifactResolutionException e) {
                    result.addMetadataResolutionException(e);
                }
            }

            fireEvent(ResolutionListener.FINISH_PROCESSING_CHILDREN, listeners, node);
        }
    }