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.function.Predicate; 024import java.util.stream.Collectors; 025import java.util.stream.Stream; 026 027import org.apache.commons.lang3.StringUtils; 028import org.apache.maven.RepositoryUtils; 029import org.apache.maven.artifact.Artifact; 030import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; 031import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 032import org.eclipse.aether.graph.DependencyNode; 033 034import static java.util.Optional.ofNullable; 035 036/** 037 * 038 * @author Robert Scholte 039 * @since 3.0.0 040 */ 041public final class ArtifactUtils { 042 043 /** 044 * Converts {@link DependencyNode} to {@link Artifact}; in comparison 045 * to {@link RepositoryUtils#toArtifact(org.eclipse.aether.artifact.Artifact)}, this method 046 * assigns {@link Artifact#getScope()} and {@link Artifact#isOptional()} based on 047 * the dependency information from the node. 048 * 049 * @param node {@link DependencyNode} to convert to {@link Artifact} 050 * @return target artifact 051 */ 052 public static Artifact toArtifact(DependencyNode node) { 053 Artifact artifact = RepositoryUtils.toArtifact(node.getArtifact()); 054 ofNullable(node.getDependency()).ifPresent(dependency -> { 055 ofNullable(dependency.getScope()).ifPresent(artifact::setScope); 056 artifact.setOptional(dependency.isOptional()); 057 }); 058 return artifact; 059 } 060 061 /** 062 * Returns a subset of dependency artifacts that match the given collection of patterns 063 * 064 * @param dependencies dependency artifacts to match against patterns 065 * @param patterns patterns to match against the artifacts 066 * @return a set containing artifacts matching one of the patterns or <code>null</code> 067 * @throws EnforcerRuleException the enforcer rule exception 068 */ 069 public static Set<Artifact> filterDependencyArtifacts(Set<Artifact> dependencies, Collection<String> patterns) 070 throws EnforcerRuleException { 071 try { 072 return ofNullable(patterns) 073 .map(collection -> collection.stream() 074 .map(p -> p.split(":")) 075 .map(StringUtils::stripAll) 076 .map(arr -> String.join(":", arr)) 077 .flatMap(pattern -> 078 dependencies.stream().filter(artifact -> compareDependency(pattern, artifact))) 079 .collect(Collectors.toSet())) 080 .orElse(null); 081 } catch (IllegalArgumentException e) { 082 if (e.getCause() instanceof InvalidVersionSpecificationException) { 083 throw new EnforcerRuleException(e.getMessage()); 084 } 085 throw e; 086 } 087 } 088 089 /** 090 * Prepares patterns directly into a reusable predicate. 091 * This can improve efficiency where there are lots of patterns and/or artifacts to match. 092 * 093 * @param patterns the patterns to use for the predicate 094 * @return a re-usable predicate. 095 */ 096 public static Predicate<Artifact> prepareDependencyArtifactMatcher(Collection<String> patterns) { 097 return cleansePatterns(patterns) 098 .map(ArtifactMatcher.Pattern::new) 099 .map(pattern -> (Predicate<Artifact>) pattern::match) 100 .reduce(Predicate::or) 101 .orElse(test -> false); 102 } 103 104 /** 105 * Cleans the patterns provided ready for use in {@link ArtifactMatcher.Pattern} 106 * 107 * @param patterns the patterns to be cleaned 108 * @return a Stream of the patterns prepared for use. 109 */ 110 private static Stream<String> cleansePatterns(Collection<String> patterns) { 111 return ofNullable(patterns) 112 .map(collection -> collection.stream() 113 .map(p -> p.split(":")) 114 .map(StringUtils::stripAll) 115 .map(arr -> String.join(":", arr))) 116 .orElse(Stream.empty()); 117 } 118 119 /** 120 * Compares the given pattern against the given artifact. The pattern should follow the format 121 * <code>groupId:artifactId:version:type:scope:classifier</code>. 122 * 123 * @param pattern The pattern to compare the artifact with. 124 * @param artifact the artifact 125 * @return <code>true</code> if the artifact matches one of the patterns 126 */ 127 public static boolean compareDependency(String pattern, Artifact artifact) { 128 return new ArtifactMatcher.Pattern(pattern).match(artifact); 129 } 130}