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