| package org.apache.maven.shared.artifact.filter; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.artifact.resolver.filter.ArtifactFilter; |
| import org.apache.maven.artifact.versioning.DefaultArtifactVersion; |
| import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException; |
| import org.apache.maven.artifact.versioning.VersionRange; |
| import org.slf4j.Logger; |
| |
| import static java.util.Objects.requireNonNull; |
| |
| /** |
| * TODO: include in maven-artifact in future |
| * |
| * @author <a href="mailto:brett@apache.org">Brett Porter</a> |
| * @see StrictPatternIncludesArtifactFilter |
| */ |
| public class PatternIncludesArtifactFilter implements ArtifactFilter, StatisticsReportingArtifactFilter |
| { |
| private static final String SEP = System.lineSeparator(); |
| |
| /** |
| * Holds the set of compiled patterns |
| */ |
| private final Set<Pattern> patterns; |
| |
| /** |
| * Whether the dependency trail should be checked |
| */ |
| private final boolean actTransitively; |
| |
| /** |
| * Set of patterns that have been triggered |
| */ |
| private final Set<Pattern> patternsTriggered = new HashSet<>(); |
| |
| /** |
| * Set of artifacts that have been filtered out |
| */ |
| private final List<Artifact> filteredArtifact = new ArrayList<>(); |
| |
| /** |
| * <p>Constructor for PatternIncludesArtifactFilter.</p> |
| * |
| * @param patterns The pattern to be used. |
| */ |
| public PatternIncludesArtifactFilter( final Collection<String> patterns ) |
| { |
| this( patterns, false ); |
| } |
| |
| /** |
| * <p>Constructor for PatternIncludesArtifactFilter.</p> |
| * |
| * @param patterns The pattern to be used. |
| * @param actTransitively transitive yes/no. |
| */ |
| public PatternIncludesArtifactFilter( final Collection<String> patterns, final boolean actTransitively ) |
| { |
| this.actTransitively = actTransitively; |
| final Set<Pattern> pat = new LinkedHashSet<>(); |
| if ( patterns != null && !patterns.isEmpty() ) |
| { |
| for ( String pattern : patterns ) |
| { |
| Pattern p = compile( pattern ); |
| pat.add( p ); |
| } |
| } |
| this.patterns = pat; |
| } |
| |
| @Override |
| public boolean include( final Artifact artifact ) |
| { |
| final boolean shouldInclude = patternMatches( artifact ); |
| |
| if ( !shouldInclude ) |
| { |
| addFilteredArtifact( artifact ); |
| } |
| |
| return shouldInclude; |
| } |
| |
| protected boolean patternMatches( final Artifact artifact ) |
| { |
| Boolean match = match( adapt( artifact ) ); |
| if ( match != null ) |
| { |
| return match; |
| } |
| |
| if ( actTransitively ) |
| { |
| final List<String> depTrail = artifact.getDependencyTrail(); |
| |
| if ( depTrail != null && depTrail.size() > 1 ) |
| { |
| for ( String trailItem : depTrail ) |
| { |
| Artifactoid artifactoid = adapt( trailItem ); |
| match = match( artifactoid ); |
| if ( match != null ) |
| { |
| return match; |
| } |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| private Boolean match( Artifactoid artifactoid ) |
| { |
| for ( Pattern pattern : patterns ) |
| { |
| if ( pattern.matches( artifactoid ) ) |
| { |
| patternsTriggered.add( pattern ); |
| return !( pattern instanceof NegativePattern ); |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * <p>addFilteredArtifact.</p> |
| * |
| * @param artifact add artifact to the filtered artifacts list. |
| */ |
| protected void addFilteredArtifact( final Artifact artifact ) |
| { |
| filteredArtifact.add( artifact ); |
| } |
| |
| @Override |
| public void reportMissedCriteria( final Logger logger ) |
| { |
| // if there are no patterns, there is nothing to report. |
| if ( !patterns.isEmpty() ) |
| { |
| final List<Pattern> missed = new ArrayList<>( patterns ); |
| missed.removeAll( patternsTriggered ); |
| |
| if ( !missed.isEmpty() && logger.isWarnEnabled() ) |
| { |
| final StringBuilder buffer = new StringBuilder(); |
| |
| buffer.append( "The following patterns were never triggered in this " ); |
| buffer.append( getFilterDescription() ); |
| buffer.append( ':' ); |
| |
| for ( Pattern pattern : missed ) |
| { |
| buffer.append( SEP ) .append( "o '" ).append( pattern ).append( "'" ); |
| } |
| |
| buffer.append( SEP ); |
| |
| logger.warn( buffer.toString() ); |
| } |
| } |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "Includes filter:" + getPatternsAsString(); |
| } |
| |
| protected String getPatternsAsString() |
| { |
| final StringBuilder buffer = new StringBuilder(); |
| for ( Pattern pattern : patterns ) |
| { |
| buffer.append( SEP ).append( "o '" ).append( pattern ).append( "'" ); |
| } |
| |
| return buffer.toString(); |
| } |
| |
| protected String getFilterDescription() |
| { |
| return "artifact inclusion filter"; |
| } |
| |
| @Override |
| public void reportFilteredArtifacts( final Logger logger ) |
| { |
| if ( !filteredArtifact.isEmpty() && logger.isDebugEnabled() ) |
| { |
| final StringBuilder buffer = new StringBuilder( |
| "The following artifacts were removed by this " + getFilterDescription() + ": " ); |
| |
| for ( Artifact artifactId : filteredArtifact ) |
| { |
| buffer.append( SEP ).append( artifactId.getId() ); |
| } |
| |
| logger.debug( buffer.toString() ); |
| } |
| } |
| |
| @Override |
| public boolean hasMissedCriteria() |
| { |
| // if there are no patterns, there is nothing to report. |
| if ( !patterns.isEmpty() ) |
| { |
| final List<Pattern> missed = new ArrayList<>( patterns ); |
| missed.removeAll( patternsTriggered ); |
| return !missed.isEmpty(); |
| } |
| |
| return false; |
| } |
| |
| private enum Coordinate |
| { |
| GROUP_ID, ARTIFACT_ID, TYPE, CLASSIFIER, BASE_VERSION |
| } |
| |
| private interface Artifactoid |
| { |
| String getCoordinate( Coordinate coordinate ); |
| } |
| |
| private static Artifactoid adapt( final Artifact artifact ) |
| { |
| requireNonNull( artifact ); |
| return coordinate -> |
| { |
| requireNonNull( coordinate ); |
| switch ( coordinate ) |
| { |
| case GROUP_ID: |
| return artifact.getGroupId(); |
| case ARTIFACT_ID: |
| return artifact.getArtifactId(); |
| case BASE_VERSION: |
| return artifact.getBaseVersion(); |
| case CLASSIFIER: |
| return artifact.hasClassifier() ? artifact.getClassifier() : null; |
| case TYPE: |
| return artifact.getType(); |
| default: |
| } |
| throw new IllegalArgumentException( "unknown coordinate: " + coordinate ); |
| }; |
| } |
| |
| /** |
| * Parses elements of {@link Artifact#getDependencyTrail()} list, they are either {@code G:A:T:V} or if artifact |
| * has classifier {@code G:A:T:C:V}, so strictly 4 or 5 segments only. |
| */ |
| private static Artifactoid adapt( final String depTrailString ) |
| { |
| requireNonNull( depTrailString ); |
| String[] coordinates = depTrailString.split( ":" ); |
| if ( coordinates.length != 4 && coordinates.length != 5 ) |
| { |
| throw new IllegalArgumentException( "Bad dep trail string: " + depTrailString ); |
| } |
| final HashMap<Coordinate, String> map = new HashMap<>(); |
| map.put( Coordinate.GROUP_ID, coordinates[0] ); |
| map.put( Coordinate.ARTIFACT_ID, coordinates[1] ); |
| map.put( Coordinate.TYPE, coordinates[2] ); |
| if ( coordinates.length == 5 ) |
| { |
| map.put( Coordinate.CLASSIFIER, coordinates[3] ); |
| map.put( Coordinate.BASE_VERSION, coordinates[4] ); |
| } |
| else |
| { |
| map.put( Coordinate.BASE_VERSION, coordinates[3] ); |
| } |
| |
| return coordinate -> |
| { |
| requireNonNull( coordinate ); |
| return map.get( coordinate ); |
| }; |
| } |
| |
| private static final String ANY = "*"; |
| |
| /** |
| * Splits the pattern string into tokens, replacing empty tokens with {@link #ANY} for patterns like {@code ::val} |
| * so it retains the position of token. |
| */ |
| private static String[] splitAndTokenize( String pattern ) |
| { |
| String[] stokens = pattern.split( ":" ); |
| String[] tokens = new String[stokens.length]; |
| for ( int i = 0; i < stokens.length; i++ ) |
| { |
| String str = stokens[i]; |
| tokens[i] = str != null && !str.isEmpty() ? str : ANY; |
| } |
| return tokens; |
| } |
| |
| /** |
| * Compiles pattern string into {@link Pattern}. |
| * |
| * TODO: patterns seems NOT documented anywhere, so best we have is source below. |
| * TODO: patterns in some cases (3, 2 tokens) seems ambiguous, we may need to clean up the specs |
| */ |
| private static Pattern compile( String pattern ) |
| { |
| if ( pattern.startsWith( "!" ) ) |
| { |
| return new NegativePattern( pattern, compile( pattern.substring( 1 ) ) ); |
| } |
| else |
| { |
| String[] tokens = splitAndTokenize( pattern ); |
| if ( tokens.length < 1 || tokens.length > 5 ) |
| { |
| throw new IllegalArgumentException( "Invalid pattern: " + pattern ); |
| } |
| |
| ArrayList<Pattern> patterns = new ArrayList<>( 5 ); |
| |
| if ( tokens.length == 5 ) |
| { |
| // trivial, full pattern w/ classifier: G:A:T:C:V |
| patterns.add( toPattern( tokens[0], Coordinate.GROUP_ID ) ); |
| patterns.add( toPattern( tokens[1], Coordinate.ARTIFACT_ID ) ); |
| patterns.add( toPattern( tokens[2], Coordinate.TYPE ) ); |
| patterns.add( toPattern( tokens[3], Coordinate.CLASSIFIER ) ); |
| patterns.add( toPattern( tokens[4], Coordinate.BASE_VERSION ) ); |
| } |
| else if ( tokens.length == 4 ) |
| { |
| // trivial, full pattern w/ version or classifier: G:A:T:V or G:A:T:C |
| patterns.add( toPattern( tokens[0], Coordinate.GROUP_ID ) ); |
| patterns.add( toPattern( tokens[1], Coordinate.ARTIFACT_ID ) ); |
| patterns.add( toPattern( tokens[2], Coordinate.TYPE ) ); |
| patterns.add( toPattern( tokens[3], Coordinate.BASE_VERSION, Coordinate.CLASSIFIER ) ); |
| } |
| else if ( tokens.length == 3 ) |
| { |
| // tricky: may be "*:artifact:*" but also "*:war:*" |
| |
| // *:*:* -> ALL |
| // *:*:xxx -> TC(xxx) |
| // *:xxx:* -> AT(xxx) |
| // *:xxx:yyy -> GA(xxx) + TC(XXX) |
| // xxx:*:* -> GA(xxx) |
| // xxx:*:yyy -> G(xxx) + TC(yyy) |
| // xxx:yyy:* -> G(xxx)+A(yyy) |
| // xxx:yyy:zzz -> G(xxx)+A(yyy)+T(zzz) |
| if ( ANY.equals( tokens[0] ) && ANY.equals( tokens[1] ) && ANY.equals( tokens[2] ) ) |
| { |
| patterns.add( MATCH_ALL_PATTERN ); |
| } |
| else if ( ANY.equals( tokens[0] ) && ANY.equals( tokens[1] ) ) |
| { |
| patterns.add( toPattern( pattern, tokens[2], Coordinate.TYPE, Coordinate.CLASSIFIER ) ); |
| } |
| else if ( ANY.equals( tokens[0] ) && ANY.equals( tokens[2] ) ) |
| { |
| patterns.add( toPattern( pattern, tokens[1], Coordinate.ARTIFACT_ID, Coordinate.TYPE ) ); |
| } |
| else if ( ANY.equals( tokens[0] ) ) |
| { |
| patterns.add( toPattern( pattern, tokens[1], Coordinate.GROUP_ID, Coordinate.ARTIFACT_ID ) ); |
| patterns.add( toPattern( pattern, tokens[2], Coordinate.TYPE, Coordinate.CLASSIFIER ) ); |
| } |
| else if ( ANY.equals( tokens[1] ) && ANY.equals( tokens[2] ) ) |
| { |
| patterns.add( toPattern( pattern, tokens[0], Coordinate.GROUP_ID, Coordinate.ARTIFACT_ID ) ); |
| } |
| else if ( ANY.equals( tokens[1] ) ) |
| { |
| patterns.add( toPattern( tokens[0], tokens[0], Coordinate.GROUP_ID ) ); |
| patterns.add( toPattern( pattern, tokens[2], Coordinate.TYPE, Coordinate.CLASSIFIER ) ); |
| } |
| else if ( ANY.equals( tokens[2] ) ) |
| { |
| patterns.add( toPattern( tokens[0], Coordinate.GROUP_ID ) ); |
| patterns.add( toPattern( tokens[1], Coordinate.ARTIFACT_ID ) ); |
| } |
| else |
| { |
| patterns.add( toPattern( tokens[0], Coordinate.GROUP_ID ) ); |
| patterns.add( toPattern( tokens[1], Coordinate.ARTIFACT_ID ) ); |
| patterns.add( toPattern( tokens[2], Coordinate.TYPE ) ); |
| } |
| |
| } |
| else if ( tokens.length == 2 ) |
| { |
| // tricky: may be "*:artifact" but also "*:war" |
| // *:* -> ALL |
| // *:xxx -> GATV(xxx) |
| // xxx:* -> G(xxx) |
| // xxx:yyy -> G(xxx)+A(yyy) |
| |
| if ( ANY.equals( tokens[0] ) && ANY.equals( tokens[1] ) ) |
| { |
| patterns.add( MATCH_ALL_PATTERN ); |
| } |
| else if ( ANY.equals( tokens[0] ) ) |
| { |
| patterns.add( toPattern( pattern, tokens[1], |
| Coordinate.GROUP_ID, Coordinate.ARTIFACT_ID, Coordinate.TYPE, Coordinate.BASE_VERSION ) ); |
| } |
| else if ( ANY.equals( tokens[1] ) ) |
| { |
| patterns.add( toPattern( tokens[0], Coordinate.GROUP_ID ) ); |
| } |
| else |
| { |
| patterns.add( toPattern( tokens[0], Coordinate.GROUP_ID ) ); |
| patterns.add( toPattern( tokens[1], Coordinate.ARTIFACT_ID ) ); |
| } |
| } |
| else |
| { |
| // trivial: G |
| patterns.add( toPattern( tokens[0], Coordinate.GROUP_ID ) ); |
| } |
| |
| // build result if needed and retains pattern string |
| if ( patterns.size() == 1 ) |
| { |
| Pattern pat = patterns.get( 0 ); |
| if ( pat == MATCH_ALL_PATTERN ) |
| { |
| return new MatchAllPattern( pattern ); |
| } |
| else |
| { |
| return pat; |
| } |
| } |
| else |
| { |
| return new AndPattern( pattern, patterns.toArray( new Pattern[0] ) ); |
| } |
| } |
| } |
| |
| private static Pattern toPattern( final String token, final Coordinate... coordinates ) |
| { |
| return toPattern( token, token, coordinates ); |
| } |
| |
| private static Pattern toPattern( final String pattern, final String token, final Coordinate... coordinates ) |
| { |
| if ( ANY.equals( token ) ) |
| { |
| return MATCH_ALL_PATTERN; |
| } |
| else |
| { |
| EnumSet<Coordinate> coordinateSet = EnumSet.noneOf( Coordinate.class ); |
| coordinateSet.addAll( Arrays.asList( coordinates ) ); |
| return new CoordinateMatchingPattern( pattern, token, coordinateSet ); |
| } |
| } |
| |
| private static final Pattern MATCH_ALL_PATTERN = new MatchAllPattern( ANY ); |
| |
| private abstract static class Pattern |
| { |
| protected final String pattern; |
| |
| private Pattern( String pattern ) |
| { |
| this.pattern = requireNonNull( pattern ); |
| } |
| |
| public abstract boolean matches( Artifactoid artifact ); |
| |
| @Override |
| public String toString() |
| { |
| return pattern; |
| } |
| } |
| |
| private static class AndPattern extends Pattern |
| { |
| private final Pattern[] patterns; |
| |
| private AndPattern( String pattern, Pattern[] patterns ) |
| { |
| super( pattern ); |
| this.patterns = patterns; |
| } |
| |
| @Override |
| public boolean matches( Artifactoid artifactoid ) |
| { |
| for ( Pattern pattern : patterns ) |
| { |
| if ( !pattern.matches( artifactoid ) ) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| private static class CoordinateMatchingPattern extends Pattern |
| { |
| private final String token; |
| |
| private final EnumSet<Coordinate> coordinates; |
| |
| private final boolean containsWildcard; |
| |
| private final boolean containsAsterisk; |
| |
| private final VersionRange optionalVersionRange; |
| |
| private CoordinateMatchingPattern( String pattern, String token, EnumSet<Coordinate> coordinates ) |
| { |
| super( pattern ); |
| this.token = token; |
| this.coordinates = coordinates; |
| this.containsAsterisk = token.contains( "*" ); |
| this.containsWildcard = this.containsAsterisk || token.contains( "?" ); |
| if ( !this.containsWildcard && coordinates.equals( EnumSet.of( Coordinate.BASE_VERSION ) ) && ( |
| token.startsWith( "[" ) || token.startsWith( "(" ) ) ) |
| { |
| try |
| { |
| this.optionalVersionRange = VersionRange.createFromVersionSpec( token ); |
| } |
| catch ( InvalidVersionSpecificationException e ) |
| { |
| throw new IllegalArgumentException( "Wrong version spec: " + token, e ); |
| } |
| } |
| else |
| { |
| this.optionalVersionRange = null; |
| } |
| } |
| |
| @Override |
| public boolean matches( Artifactoid artifactoid ) |
| { |
| for ( Coordinate coordinate : coordinates ) |
| { |
| String value = artifactoid.getCoordinate( coordinate ); |
| if ( Coordinate.BASE_VERSION == coordinate && optionalVersionRange != null ) |
| { |
| if ( optionalVersionRange.containsVersion( new DefaultArtifactVersion( value ) ) ) |
| { |
| return true; |
| } |
| } |
| else if ( containsWildcard ) |
| { |
| if ( match( token, containsAsterisk, value ) ) |
| { |
| return true; |
| } |
| } |
| else |
| { |
| if ( token.equals( value ) ) |
| { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * Matches all input |
| */ |
| private static class MatchAllPattern extends Pattern |
| { |
| private MatchAllPattern( String pattern ) |
| { |
| super( pattern ); |
| } |
| |
| @Override |
| public boolean matches( Artifactoid artifactoid ) |
| { |
| return true; |
| } |
| } |
| |
| /** |
| * Negative pattern |
| */ |
| private static class NegativePattern extends Pattern |
| { |
| private final Pattern inner; |
| |
| private NegativePattern( String pattern, Pattern inner ) |
| { |
| super( pattern ); |
| this.inner = inner; |
| } |
| |
| @Override |
| public boolean matches( Artifactoid artifactoid ) |
| { |
| return inner.matches( artifactoid ); |
| } |
| } |
| |
| // this beauty below must be salvaged |
| |
| @SuppressWarnings( "InnerAssignment" ) |
| private static boolean match( final String pattern, final boolean containsAsterisk, final String value ) |
| { |
| char[] patArr = pattern.toCharArray(); |
| char[] strArr = value != null ? value.toCharArray() : new char[0]; |
| int patIdxStart = 0; |
| int patIdxEnd = patArr.length - 1; |
| int strIdxStart = 0; |
| int strIdxEnd = strArr.length - 1; |
| char ch; |
| |
| if ( !containsAsterisk ) |
| { |
| // No '*'s, so we make a shortcut |
| if ( patIdxEnd != strIdxEnd ) |
| { |
| return false; // Pattern and string do not have the same size |
| } |
| for ( int i = 0; i <= patIdxEnd; i++ ) |
| { |
| ch = patArr[i]; |
| if ( ch != '?' && ch != strArr[i] ) |
| { |
| return false; // Character mismatch |
| } |
| } |
| return true; // String matches against pattern |
| } |
| |
| if ( patIdxEnd == 0 ) |
| { |
| return true; // Pattern contains only '*', which matches anything |
| } |
| |
| // Process characters before first star |
| while ( ( ch = patArr[patIdxStart] ) != '*' && strIdxStart <= strIdxEnd ) |
| { |
| if ( ch != '?' && ch != strArr[strIdxStart] ) |
| { |
| return false; // Character mismatch |
| } |
| patIdxStart++; |
| strIdxStart++; |
| } |
| if ( strIdxStart > strIdxEnd ) |
| { |
| // All characters in the string are used. Check if only '*'s are |
| // left in the pattern. If so, we succeeded. Otherwise failure. |
| for ( int i = patIdxStart; i <= patIdxEnd; i++ ) |
| { |
| if ( patArr[i] != '*' ) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // Process characters after last star |
| while ( ( ch = patArr[patIdxEnd] ) != '*' && strIdxStart <= strIdxEnd ) |
| { |
| if ( ch != '?' && ch != strArr[strIdxEnd] ) |
| { |
| return false; // Character mismatch |
| } |
| patIdxEnd--; |
| strIdxEnd--; |
| } |
| if ( strIdxStart > strIdxEnd ) |
| { |
| // All characters in the string are used. Check if only '*'s are |
| // left in the pattern. If so, we succeeded. Otherwise failure. |
| for ( int i = patIdxStart; i <= patIdxEnd; i++ ) |
| { |
| if ( patArr[i] != '*' ) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| // process pattern between stars. padIdxStart and patIdxEnd point |
| // always to a '*'. |
| while ( patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd ) |
| { |
| int patIdxTmp = -1; |
| for ( int i = patIdxStart + 1; i <= patIdxEnd; i++ ) |
| { |
| if ( patArr[i] == '*' ) |
| { |
| patIdxTmp = i; |
| break; |
| } |
| } |
| if ( patIdxTmp == patIdxStart + 1 ) |
| { |
| // Two stars next to each other, skip the first one. |
| patIdxStart++; |
| continue; |
| } |
| // Find the pattern between padIdxStart & padIdxTmp in str between |
| // strIdxStart & strIdxEnd |
| int patLength = ( patIdxTmp - patIdxStart - 1 ); |
| int strLength = ( strIdxEnd - strIdxStart + 1 ); |
| int foundIdx = -1; |
| strLoop: |
| for ( int i = 0; i <= strLength - patLength; i++ ) |
| { |
| for ( int j = 0; j < patLength; j++ ) |
| { |
| ch = patArr[patIdxStart + j + 1]; |
| if ( ch != '?' && ch != strArr[strIdxStart + i + j] ) |
| { |
| continue strLoop; |
| } |
| } |
| |
| foundIdx = strIdxStart + i; |
| break; |
| } |
| |
| if ( foundIdx == -1 ) |
| { |
| return false; |
| } |
| |
| patIdxStart = patIdxTmp; |
| strIdxStart = foundIdx + patLength; |
| } |
| |
| // All characters in the string are used. Check if only '*'s are left |
| // in the pattern. If so, we succeeded. Otherwise failure. |
| for ( int i = patIdxStart; i <= patIdxEnd; i++ ) |
| { |
| if ( patArr[i] != '*' ) |
| { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |