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<Pattern>();
160
161    private Collection<Pattern> ignorePatterns = new LinkedList<Pattern>();
162
163    /**
164     * Construct class by providing patterns as strings. Empty strings are ignored.
165     * 
166     * @throws NullPointerException if any of the arguments is null
167     */
168    public ArtifactMatcher( final Collection<String> patterns, final Collection<String> ignorePatterns )
169    {
170        if ( patterns == null )
171        {
172            throw new NullPointerException( "patterns" );
173        }
174        if ( ignorePatterns == null )
175        {
176            throw new NullPointerException( "ignorePatterns" );
177        }
178        for ( String pattern : patterns )
179        {
180            if ( pattern != null && !"".equals( pattern ) )
181            {
182                this.patterns.add( new Pattern( pattern ) );
183            }
184        }
185
186        for ( String ignorePattern : ignorePatterns )
187        {
188            if ( ignorePattern != null && !"".equals( ignorePattern ) )
189            {
190                this.ignorePatterns.add( new Pattern( ignorePattern ) );
191            }
192        }
193    }
194
195    /**
196     * Check if artifact matches patterns.
197     * 
198     * @throws InvalidVersionSpecificationException
199     */
200    public boolean match( Artifact artifact )
201        throws InvalidVersionSpecificationException
202    {
203        for ( Pattern pattern : patterns )
204        {
205            if ( pattern.match( artifact ) )
206            {
207                for ( Pattern ignorePattern : ignorePatterns )
208                {
209                    if ( ignorePattern.match( artifact ) )
210                    {
211                        return false;
212                    }
213                }
214                return true;
215            }
216        }
217        return false;
218    }
219}