001package org.apache.maven.plugins.enforcer;
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.commons.lang.StringUtils;
023import org.apache.maven.artifact.Artifact;
024import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
025import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
026import org.apache.maven.plugin.logging.Log;
027import org.apache.maven.plugins.enforcer.utils.ArtifactMatcher;
028import org.apache.maven.plugins.enforcer.utils.ArtifactMatcher.Pattern;
029import java.util.HashSet;
030import java.util.List;
031import java.util.Set;
032
033/**
034 * This rule checks that lists of dependencies are not included.
035 * 
036 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
037 * @version $Id: BannedDependencies.java 1697215 2015-08-23 16:27:17Z khmarbaise $
038 */
039public class BannedDependencies
040    extends AbstractBanDependencies
041{
042
043    /**
044     * Specify the banned dependencies. This can be a list of artifacts in the format
045     * <code>groupId[:artifactId][:version]</code>. Any of the sections can be a wildcard 
046     * by using '*' (ie group:*:1.0) <br>
047     * The rule will fail if any dependency matches any exclude, unless it also matches 
048     * an include rule.
049     * 
050     * @see {@link #setExcludes(List)}
051     * @see {@link #getExcludes()}
052     */
053    private List<String> excludes = null;
054
055    /**
056     * Specify the allowed dependencies. This can be a list of artifacts in the format
057     * <code>groupId[:artifactId][:version]</code>. Any of the sections can be a wildcard 
058     * by using '*' (ie group:*:1.0) <br>
059     * Includes override the exclude rules. It is meant to allow wide exclusion rules 
060     * with wildcards and still allow a
061     * smaller set of includes. <br>
062     * For example, to ban all xerces except xerces-api -> exclude "xerces", include "xerces:xerces-api"
063     * 
064     * @see {@link #setIncludes(List)}
065     * @see {@link #getIncludes()}
066     */
067    private List<String> includes = null;
068
069    /**
070     * {@inheritDoc}
071     */
072    protected Set<Artifact> checkDependencies( Set<Artifact> theDependencies, Log log )
073        throws EnforcerRuleException
074    {
075
076        Set<Artifact> excluded = checkDependencies( theDependencies, excludes );
077
078        // anything specifically included should be removed
079        // from the ban list.
080        if ( excluded != null )
081        {
082            Set<Artifact> included = checkDependencies( theDependencies, includes );
083
084            if ( included != null )
085            {
086                excluded.removeAll( included );
087            }
088        }
089        return excluded;
090
091    }
092
093    /**
094     * Checks the set of dependencies against the list of patterns.
095     * 
096     * @param thePatterns the patterns
097     * @param dependencies the dependencies
098     * @return a set containing artifacts matching one of the patterns or <code>null</code>
099     * @throws EnforcerRuleException the enforcer rule exception
100     */
101    private Set<Artifact> checkDependencies( Set<Artifact> dependencies, List<String> thePatterns )
102        throws EnforcerRuleException
103    {
104        Set<Artifact> foundMatches = null;
105
106        if ( thePatterns != null && thePatterns.size() > 0 )
107        {
108
109            for ( String pattern : thePatterns )
110            {
111                String[] subStrings = pattern.split( ":" );
112                subStrings = StringUtils.stripAll( subStrings );
113                String resultPattern = StringUtils.join( subStrings, ":" );
114
115                for ( Artifact artifact : dependencies )
116                {
117                    if ( compareDependency( resultPattern, artifact ) )
118                    {
119                        // only create if needed
120                        if ( foundMatches == null )
121                        {
122                            foundMatches = new HashSet<Artifact>();
123                        }
124                        foundMatches.add( artifact );
125                    }
126                }
127            }
128        }
129        return foundMatches;
130    }
131
132    /**
133     * Compares the given pattern against the given artifact. The pattern should follow the format
134     * <code>groupId:artifactId:version:type:scope:classifier</code>.
135     * 
136     * @param pattern The pattern to compare the artifact with.
137     * @param artifact the artifact
138     * @return <code>true</code> if the artifact matches one of the patterns
139     * @throws EnforcerRuleException the enforcer rule exception
140     */
141    protected boolean compareDependency( String pattern, Artifact artifact )
142        throws EnforcerRuleException
143    {
144
145        ArtifactMatcher.Pattern am = new Pattern( pattern );
146        boolean result;
147        try
148        {
149            result = am.match( artifact );
150        }
151        catch ( InvalidVersionSpecificationException e )
152        {
153            throw new EnforcerRuleException( "Invalid Version Range: ", e );
154        }
155
156        return result;
157    }
158
159    /**
160     * Gets the excludes.
161     * 
162     * @return the excludes
163     */
164    public List<String> getExcludes()
165    {
166        return this.excludes;
167    }
168
169    /**
170     * Specify the banned dependencies. This can be a list of artifacts in the format
171     * <code>groupId[:artifactId][:version]</code>. Any of the sections can be a wildcard 
172     * by using '*' (ie group:*:1.0) <br>
173     * The rule will fail if any dependency matches any exclude, unless it also matches an 
174     * include rule.
175     * 
176     * @see #getExcludes()
177     * @param theExcludes the excludes to set
178     */
179    public void setExcludes( List<String> theExcludes )
180    {
181        this.excludes = theExcludes;
182    }
183
184    /**
185     * Gets the includes.
186     * 
187     * @return the includes
188     */
189    public List<String> getIncludes()
190    {
191        return this.includes;
192    }
193
194    /**
195     * Specify the allowed dependencies. This can be a list of artifacts in the format
196     * <code>groupId[:artifactId][:version]</code>. Any of the sections can be a wildcard 
197     * by using '*' (ie group:*:1.0) <br>
198     * Includes override the exclude rules. It is meant to allow wide exclusion rules with 
199     * wildcards and still allow a
200     * smaller set of includes. <br>
201     * For example, to ban all xerces except xerces-api -> exclude "xerces", 
202     * include "xerces:xerces-api"
203     * 
204     * @see #setIncludes(List)
205     * @param theIncludes the includes to set
206     */
207    public void setIncludes( List<String> theIncludes )
208    {
209        this.includes = theIncludes;
210    }
211
212}