001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.enforcer.rules.utils;
020
021import java.util.Collection;
022import java.util.Set;
023import java.util.stream.Collectors;
024
025import org.apache.commons.lang3.StringUtils;
026import org.apache.maven.RepositoryUtils;
027import org.apache.maven.artifact.Artifact;
028import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
029import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
030import org.eclipse.aether.graph.DependencyNode;
031
032import static java.util.Optional.ofNullable;
033
034/**
035 *
036 * @author Robert Scholte
037 * @since 3.0.0
038 */
039public final class ArtifactUtils {
040
041    /**
042     * Converts {@link DependencyNode} to {@link Artifact}; in comparison
043     * to {@link RepositoryUtils#toArtifact(org.eclipse.aether.artifact.Artifact)}, this method
044     * assigns {@link Artifact#getScope()} and {@link Artifact#isOptional()} based on
045     * the dependency information from the node.
046     *
047     * @param node {@link DependencyNode} to convert to {@link Artifact}
048     * @return target artifact
049     */
050    public static Artifact toArtifact(DependencyNode node) {
051        Artifact artifact = RepositoryUtils.toArtifact(node.getArtifact());
052        ofNullable(node.getDependency()).ifPresent(dependency -> {
053            ofNullable(dependency.getScope()).ifPresent(artifact::setScope);
054            artifact.setOptional(dependency.isOptional());
055        });
056        return artifact;
057    }
058
059    /**
060     * Returns a subset of dependency artifacts that match the given collection of patterns
061     *
062     * @param dependencies dependency artifacts to match against patterns
063     * @param patterns patterns to match against the artifacts
064     * @return a set containing artifacts matching one of the patterns or <code>null</code>
065     * @throws EnforcerRuleException the enforcer rule exception
066     */
067    public static Set<Artifact> filterDependencyArtifacts(Set<Artifact> dependencies, Collection<String> patterns)
068            throws EnforcerRuleException {
069        try {
070            return ofNullable(patterns)
071                    .map(collection -> collection.stream()
072                            .map(p -> p.split(":"))
073                            .map(StringUtils::stripAll)
074                            .map(arr -> String.join(":", arr))
075                            .flatMap(pattern ->
076                                    dependencies.stream().filter(artifact -> compareDependency(pattern, artifact)))
077                            .collect(Collectors.toSet()))
078                    .orElse(null);
079        } catch (IllegalArgumentException e) {
080            if (e.getCause() instanceof InvalidVersionSpecificationException) {
081                throw new EnforcerRuleException(e.getMessage());
082            }
083            throw e;
084        }
085    }
086
087    /**
088     * Checks if the given dependency artifact matches the given collection of patterns
089     *
090     * @param artifact dependency artifact to match against patterns
091     * @param patterns patterns to match against the artifacts
092     * @return {@code true} if the given artifact matches the set of patterns
093     */
094    public static boolean matchDependencyArtifact(Artifact artifact, Collection<String> patterns) {
095        try {
096            return ofNullable(patterns)
097                    .map(collection -> collection.stream()
098                            .map(p -> p.split(":"))
099                            .map(StringUtils::stripAll)
100                            .map(arr -> String.join(":", arr))
101                            .anyMatch(pattern -> compareDependency(pattern, artifact)))
102                    .orElse(false);
103        } catch (IllegalArgumentException e) {
104            if (e.getCause() instanceof InvalidVersionSpecificationException) {
105                throw new IllegalArgumentException(e.getMessage());
106            }
107            throw e;
108        }
109    }
110
111    /**
112     * Compares the given pattern against the given artifact. The pattern should follow the format
113     * <code>groupId:artifactId:version:type:scope:classifier</code>.
114     *
115     * @param pattern The pattern to compare the artifact with.
116     * @param artifact the artifact
117     * @return <code>true</code> if the artifact matches one of the patterns
118     */
119    public static boolean compareDependency(String pattern, Artifact artifact) {
120        return new ArtifactMatcher.Pattern(pattern).match(artifact);
121    }
122}