001package org.apache.maven.plugins.enforcer.utils; 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 org.apache.maven.artifact.Artifact; 023import org.apache.maven.artifact.versioning.DefaultArtifactVersion; 024import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; 025import org.apache.maven.artifact.versioning.VersionRange; 026import org.apache.maven.plugins.enforcer.AbstractVersionEnforcer; 027import java.util.Collection; 028import java.util.LinkedList; 029 030/** 031 * This class is used for matching Artifacts against a list of patterns. 032 * 033 * @author Jakub Senko 034 * @see org.apache.maven.plugins.enforcer.BanTransitiveDependencies 035 */ 036public final class ArtifactMatcher 037{ 038 039 /** 040 * @author I don't know 041 */ 042 public static class Pattern 043 { 044 private String pattern; 045 046 private String[] parts; 047 048 public Pattern( String pattern ) 049 { 050 if ( pattern == null ) 051 { 052 throw new NullPointerException( "pattern" ); 053 } 054 055 this.pattern = pattern; 056 057 parts = pattern.split( ":", 7 ); 058 059 if ( parts.length == 7 ) 060 { 061 throw new IllegalArgumentException( "Pattern contains too many delimiters." ); 062 } 063 064 for ( String part : parts ) 065 { 066 if ( "".equals( part ) ) 067 { 068 throw new IllegalArgumentException( "Pattern or its part is empty." ); 069 } 070 } 071 } 072 073 public boolean match( Artifact artifact ) 074 throws InvalidVersionSpecificationException 075 { 076 if ( artifact == null ) 077 { 078 throw new NullPointerException( "artifact" ); 079 } 080 081 switch ( parts.length ) 082 { 083 case 6: 084 String classifier = artifact.getClassifier(); 085 if ( !matches( parts[5], classifier ) ) 086 { 087 return false; 088 } 089 case 5: 090 String scope = artifact.getScope(); 091 if ( scope == null || scope.equals( "" ) ) 092 { 093 scope = Artifact.SCOPE_COMPILE; 094 } 095 096 if ( !matches( parts[4], scope ) ) 097 { 098 return false; 099 } 100 case 4: 101 String type = artifact.getType(); 102 if ( type == null || type.equals( "" ) ) 103 { 104 type = "jar"; 105 } 106 107 if ( !matches( parts[3], type ) ) 108 { 109 return false; 110 } 111 112 case 3: 113 if ( !matches( parts[2], artifact.getVersion() ) ) 114 { 115 // CHECKSTYLE_OFF: LineLength 116 if ( !AbstractVersionEnforcer.containsVersion( VersionRange.createFromVersionSpec( parts[2] ), 117 new DefaultArtifactVersion( 118 artifact.getVersion() ) ) ) 119 // CHECKSTYLE_ON: LineLength 120 { 121 return false; 122 } 123 } 124 125 case 2: 126 if ( !matches( parts[1], artifact.getArtifactId() ) ) 127 { 128 return false; 129 } 130 case 1: 131 return matches( parts[0], artifact.getGroupId() ); 132 default: 133 throw new AssertionError(); 134 } 135 } 136 137 private boolean matches( String expression, String input ) 138 { 139 String regex = 140 expression.replace( ".", "\\." ).replace( "*", ".*" ).replace( ":", "\\:" ).replace( '?', '.' ) 141 .replace( "[", "\\[" ).replace( "]", "\\]" ).replace( "(", "\\(" ).replace( ")", "\\)" ); 142 143 // TODO: Check if this can be done better or prevented earlier. 144 if ( input == null ) 145 { 146 input = ""; 147 } 148 149 return java.util.regex.Pattern.matches( regex, input ); 150 } 151 152 @Override 153 public String toString() 154 { 155 return pattern; 156 } 157 } 158 159 private Collection<Pattern> patterns = new LinkedList<>(); 160 161 private Collection<Pattern> ignorePatterns = new LinkedList<>(); 162 163 /** 164 * Construct class by providing patterns as strings. Empty strings are ignored. 165 * 166 * @param patterns includes 167 * @param ignorePatterns excludes 168 * @throws NullPointerException if any of the arguments is null 169 */ 170 public ArtifactMatcher( final Collection<String> patterns, final Collection<String> ignorePatterns ) 171 { 172 if ( patterns == null ) 173 { 174 throw new NullPointerException( "patterns" ); 175 } 176 if ( ignorePatterns == null ) 177 { 178 throw new NullPointerException( "ignorePatterns" ); 179 } 180 for ( String pattern : patterns ) 181 { 182 if ( pattern != null && !"".equals( pattern ) ) 183 { 184 this.patterns.add( new Pattern( pattern ) ); 185 } 186 } 187 188 for ( String ignorePattern : ignorePatterns ) 189 { 190 if ( ignorePattern != null && !"".equals( ignorePattern ) ) 191 { 192 this.ignorePatterns.add( new Pattern( ignorePattern ) ); 193 } 194 } 195 } 196 197 /** 198 * Check if artifact matches patterns. 199 * 200 * @param artifact the artifact to match 201 * @return {@code true} if artifact matches any {@link #patterns} and none of the {@link #ignorePatterns}, otherwise 202 * {@code false} 203 * @throws InvalidVersionSpecificationException if any pattern contains an invalid version range 204 */ 205 public boolean match( Artifact artifact ) 206 throws InvalidVersionSpecificationException 207 { 208 for ( Pattern pattern : patterns ) 209 { 210 if ( pattern.match( artifact ) ) 211 { 212 for ( Pattern ignorePattern : ignorePatterns ) 213 { 214 if ( ignorePattern.match( artifact ) ) 215 { 216 return false; 217 } 218 } 219 return true; 220 } 221 } 222 return false; 223 } 224}