/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.commons.io.res.util; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import lucee.commons.lang.StringUtil; /** * a WildcardPattern that accepts a comma- (or semi-colon-) separated value of patterns, e.g. "*.gif, *.jpg, *.jpeg, *.png" * and an optional isExclude boolean value which negates the results of the default implementation * * also, lines 31 - 35 allow to set isExclude to true by passing a pattern whose first character is an exclamation point '!' * * @author Igal */ public class WildcardPattern { private final String pattern; private final boolean isInclude; private final List<ParsedPattern> patterns; /** * * @param pattern - the wildcard pattern, or a comma/semi-colon separated value of wildcard patterns * @param isCaseSensitive - if true, does a case-sensitive matching * @param isExclude - if true, the filter becomes an Exclude filter so that only items that do not match the pattern are accepted */ public WildcardPattern( String pattern, boolean isCaseSensitive, boolean isExclude, String delimiters ) { if ( pattern.charAt( 0 ) == '!' ) { // set isExclude to true if the first char of pattern is an exclamation point '!' pattern = pattern.substring( 1 ); isExclude = true; } this.pattern = pattern; this.isInclude = !isExclude; StringTokenizer tokenizer = new StringTokenizer( pattern, !StringUtil.isEmpty(delimiters, true) ? delimiters : "|" ); patterns = new ArrayList<ParsedPattern>(); while ( tokenizer.hasMoreTokens() ) { String token = tokenizer.nextToken().trim(); if ( !token.isEmpty() ) patterns.add( new ParsedPattern( token, isCaseSensitive ) ); } } /** calls this( pattern, isCaseSensitive, false, delimiters ); */ public WildcardPattern( String pattern, boolean isCaseSensitive, String delimiters ) { this( pattern, isCaseSensitive, false, delimiters ); } public boolean isMatch( String input ) { for ( ParsedPattern pp : this.patterns ) { if (pp.isMatch(input)) return isInclude; } return !isInclude; } @Override public String toString() { return "WildcardPattern: " + pattern; } public static class ParsedPattern { public final static String MATCH_ANY = "*"; public final static String MATCH_ONE = "?"; private String[] parts; private final boolean isCaseSensitive; public ParsedPattern( String pattern, boolean isCaseSensitive ) { this.isCaseSensitive = isCaseSensitive; if (!isCaseSensitive) pattern = pattern.toLowerCase(); List<String> lsp = new ArrayList<String>(); int len = pattern.length(); int subStart = 0; for (int i=subStart; i<len; i++) { char c = pattern.charAt( i ); if (c == '*' || c == '?') { if (i > subStart) lsp.add( pattern.substring( subStart, i ) ); lsp.add(c == '*' ? MATCH_ANY : MATCH_ONE); subStart = i + 1; } } if (len > subStart) lsp.add(pattern.substring(subStart)); this.parts = lsp.toArray(new String[ lsp.size()] ); } /** calls this( pattern, false, false ); */ public ParsedPattern( String pattern ) { this( pattern, false ); } /** tests if the input string matches the pattern */ public boolean isMatch( String input ) { if ( !isCaseSensitive ) input = input.toLowerCase(); if (parts.length == 1) return ( parts[0] == MATCH_ANY || parts[0].equals(input) ); if (parts.length == 2) { if (parts[0] == MATCH_ANY) return input.endsWith( parts[1] ); if (parts[ parts.length - 1 ] == MATCH_ANY) return input.startsWith( parts[0] ); } int pos = 0; int len = input.length(); boolean doMatchAny = false; for (String part : parts) { if ( part == MATCH_ANY ) { doMatchAny = true; continue; } if ( part == MATCH_ONE ) { doMatchAny = false; pos++; continue; } int ix = input.indexOf( part, pos ); if ( ix == -1 ) return false; if ( !doMatchAny && ix != pos ) return false; pos = ix + part.length(); doMatchAny = false; } if ( (parts[ parts.length - 1 ] != MATCH_ANY) && (len != pos) ) // if pattern doesn't end with * then we shouldn't have any more characters in input return false; return true; } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (String s : parts) sb.append( s ); return sb.toString(); } } }