kitaev@213: /* kitaev@213: * Copyright (c) 2011 TMate Software Ltd kitaev@213: * kitaev@213: * This program is free software; you can redistribute it and/or modify kitaev@213: * it under the terms of the GNU General Public License as published by kitaev@213: * the Free Software Foundation; version 2 of the License. kitaev@213: * kitaev@213: * This program is distributed in the hope that it will be useful, kitaev@213: * but WITHOUT ANY WARRANTY; without even the implied warranty of kitaev@213: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the kitaev@213: * GNU General Public License for more details. kitaev@213: * kitaev@213: * For information on how to redistribute this software under kitaev@213: * the terms of a license other than GNU General Public License kitaev@213: * contact TMate Software at support@hg4j.com kitaev@213: */ kitaev@213: package org.tmatesoft.hg.internal; kitaev@213: kitaev@213: import java.util.regex.PatternSyntaxException; kitaev@213: kitaev@213: import org.tmatesoft.hg.util.Path; kitaev@213: kitaev@213: /** kitaev@213: * kitaev@213: * @author Artem Tikhomirov kitaev@213: * @author TMate Software Ltd. kitaev@213: */ kitaev@213: public class PathGlobMatcher implements Path.Matcher { kitaev@213: kitaev@213: private final PathRegexpMatcher delegate; kitaev@213: kitaev@213: /** kitaev@213: * kitaev@213: * @param globPatterns kitaev@213: * @throws NullPointerException if argument is null kitaev@213: * @throws IllegalArgumentException if any of the patterns is not valid kitaev@213: */ kitaev@213: public PathGlobMatcher(String... globPatterns) { kitaev@213: String[] regexp = new String[globPatterns.length]; //deliberately let fail with NPE kitaev@213: int i = 0; kitaev@213: for (String s : globPatterns) { kitaev@213: regexp[i] = glob2regexp(s); kitaev@213: } kitaev@213: try { kitaev@213: delegate = new PathRegexpMatcher(regexp); kitaev@213: } catch (PatternSyntaxException ex) { kitaev@213: ex.printStackTrace(); kitaev@213: throw new IllegalArgumentException(ex); kitaev@213: } kitaev@213: } kitaev@213: kitaev@213: kitaev@213: // HgIgnore.glob2regex is similar, but IsIgnore solves slightly different task kitaev@213: // (need to match partial paths, e.g. for glob 'bin' shall match not only 'bin' folder, but also any path below it, kitaev@213: // which is not generally the case kitaev@213: private static String glob2regexp(String glob) { kitaev@213: int end = glob.length() - 1; kitaev@213: boolean needLineEndMatch = glob.charAt(end) != '*'; kitaev@213: while (end > 0 && glob.charAt(end) == '*') end--; // remove trailing * that are useless for Pattern.find() kitaev@213: StringBuilder sb = new StringBuilder(end*2); kitaev@213: if (glob.charAt(0) != '*') { kitaev@213: sb.append('^'); kitaev@213: } kitaev@213: for (int i = 0; i <= end; i++) { kitaev@213: char ch = glob.charAt(i); kitaev@213: if (ch == '*') { kitaev@213: if (glob.charAt(i+1) == '*') { // i < end because we've stripped any trailing * earlier kitaev@213: // any char, including path segment separator kitaev@213: sb.append(".*?"); kitaev@213: i++; kitaev@213: } else { kitaev@213: // just path segments kitaev@213: sb.append("[^/]*?"); kitaev@213: } kitaev@213: continue; kitaev@213: } else if (ch == '?') { kitaev@213: sb.append("[^/]"); kitaev@213: continue; kitaev@213: } else if (ch == '.' || ch == '\\') { kitaev@213: sb.append('\\'); kitaev@213: } kitaev@213: sb.append(ch); kitaev@213: } kitaev@213: if (needLineEndMatch) { kitaev@213: sb.append('$'); kitaev@213: } kitaev@213: return sb.toString(); kitaev@213: } kitaev@213: kitaev@213: public boolean accept(Path path) { kitaev@213: return delegate.accept(path); kitaev@213: } kitaev@213: kitaev@213: }