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