/*
* RHQ Management Platform
* Copyright (C) 2005-2009 Red Hat, Inc.
* All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation, and/or the GNU Lesser
* General Public License, version 2.1, also as published by the Free
* Software Foundation.
*
* This program 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 General Public License and the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License
* and the GNU Lesser General Public License along with this program;
* if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.rhq.augeas.util;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A helper class for easy work with glob patterns.
*
* @author Lukas Krejci
*/
public class Glob {
private static final Log log = LogFactory.getLog(Glob.class);
public static final Comparator<File> ALPHABETICAL_COMPARATOR = new Comparator<File>() {
public int compare(File o1, File o2) {
String path1 = o1.getAbsolutePath();
String path2 = o2.getAbsolutePath();
return path1.compareTo(path2);
}
};
private Glob() {
}
/**
* Checks whether the provided string is a wildcard glob pattern.
*
* @param globPattern
* @return
*/
public static boolean isWildcard(String globPattern) {
for(char specialChar : GlobFilter.WILDCARD_CHARS) {
if (globPattern.indexOf(specialChar) >= 0) return true;
}
return false;
}
/**
* Checks if the file matches the glob pattern with given path as a root of the filesystem.
*
* If the glob pattern denotes an absolute path, it is understood to be under the supplied
* fs root.
*
* @param rootPath
* @param globPattern
* @param file
* @return
*/
public static boolean matches(File rootPath, String globPattern, File file) {
String rootPortion = rootPortion(globPattern);
globPattern = globPattern.substring(rootPortion.length());
globPattern = new File(rootPath, globPattern).getAbsolutePath();
return new GlobFilter(globPattern).accept(file);
}
/**
* This is an overloaded version of the {@link #match(File, String, Comparator)} method
* that passes <code>null</code> as the comparator to use.
*
* @see #match(File, String, Comparator)
*/
public static List<File> match(File parentPath, String globPattern) {
return match(parentPath, globPattern, null);
}
/**
* Returns a fixed size list of matches.
*
* The parent path specifies the "root" from which the glob pattern applies.
* The glob pattern can span several directories with wildcards present only
* on the lowest level.
* The glob pattern is always relative to the specified parent path, even if it denotes
* an absolute path. In that case, the leading root path is chopped off and the rest is
* appended to the parent path.
*
* @param parentPath the parent path to start the pattern search
* @param globPattern the glob pattern to match against
* @param resultComparator the comparator using which to sort the results or null if no sorting is necessary
* @return the list of matches
*/
public static List<File> match(File parentPath, String globPattern, Comparator<? super File> resultComparator) {
if (!parentPath.exists()) {
throw new IllegalArgumentException("Path '" + parentPath + "' does not exist.");
}
if (!parentPath.isDirectory()) {
throw new IllegalArgumentException("Path '" + parentPath + "' is not a directory.");
}
String rootPortion = rootPortion(globPattern);
globPattern = globPattern.substring(rootPortion.length());
//now search for the first special character in the patterns
int specialCharIdx = globPattern.length();
for(char specialChar : GlobFilter.WILDCARD_CHARS) {
int idx = globPattern.indexOf(specialChar);
if (idx >= 0 && specialCharIdx > idx) {
specialCharIdx = idx;
}
}
if (specialCharIdx > 0) {
//now search for the first path separator preceding the special char
int globParentIdx = globPattern.lastIndexOf(File.separatorChar, specialCharIdx);
if (globParentIdx > 0) {
//move the parent path down to the nearest parent of the wildcard part of the
//glob pattern
parentPath = new File(parentPath, globPattern.substring(0, globParentIdx));
globPattern = createGlobPattern(parentPath.getAbsolutePath(), globPattern.substring(globParentIdx));
} else {
globPattern = createGlobPattern(parentPath.getAbsolutePath(),globPattern);
}
} else {
globPattern = createGlobPattern(parentPath.getAbsolutePath(),globPattern);
}
globPattern = new File(globPattern).getAbsolutePath();
File[] files = parentPath.listFiles(new GlobFilter(globPattern));
if (files == null) {
log.debug("Could list files in " + parentPath);
return Collections.emptyList();
}
if (resultComparator != null) {
Arrays.sort(files, resultComparator);
}
return Arrays.asList(files);
}
private static String createGlobPattern(String parent,String pattern){
if (!parent.endsWith(File.separator))
parent = parent + File.separatorChar;
return parent+pattern;
}
/**
* This is an overloaded version of the {@link #matchAll(File, List, Comparator)} method
* that passes <code>null</code> as the comparator to use.
*
* @see #matchAll(File, List, Comparator)
*/
public static List<File> matchAll(File parentPath, List<String> globPatterns) {
return matchAll(parentPath, globPatterns, null);
}
public static List<File> matchAll(File parentPath, List<String> globPatterns, Comparator<? super File> resultComparator) {
ArrayList<File> matches = new ArrayList<File>();
for(String p : globPatterns) {
matches.addAll(match(parentPath, p, null));
}
if (resultComparator != null) {
Collections.sort(matches, resultComparator);
}
return matches;
}
public static void exclude(List<File> matches, String globPattern) {
GlobFilter filter = new GlobFilter(globPattern);
Iterator<File> it = matches.iterator();
while (it.hasNext()) {
if (filter.accept(it.next())) {
it.remove();
}
}
}
public static void excludeAll(List<File> matches, String... globPattern) {
excludeAll(matches, Arrays.asList(globPattern));
}
public static void excludeAll(List<File> matches, List<String> globPatterns) {
for(String p : globPatterns) {
exclude(matches, p);
}
}
public static String rootPortion(String path) {
// On Windows, make sure we have the correct drive letter format and slash direction for comparison.
if (File.separator.equals("\\")) {
try {
File f = new File(path);
path = f.isAbsolute() ? f.getCanonicalPath() : f.getPath();
} catch (Exception e) {
// should not happen but just leave path as-is
}
}
File[] roots = File.listRoots();
if (roots != null) {
for (File root : roots) {
if (path.startsWith(root.getPath())) {
return root.getPath();
}
}
} else {
log.warn("Could not determine file system roots. This is strange.");
}
return "";
}
}