/* * Written by Dawid Kurzyniec and released to the public domain, as explained * at http://creativecommons.org/licenses/publicdomain */ package edu.emory.mathcs.util.classloader; import java.util.regex.*; import java.net.*; /** * Utility methods related to remote resource access. * * @author Dawid Kurzyniec * @version 1.0 */ public final class ResourceUtils { private static final Pattern dotInMiddlePattern = Pattern.compile("/\\./"); private static final Pattern dotAtBegPattern = Pattern.compile("^\\./"); private static final Pattern dotAtEndPattern = Pattern.compile("/\\.$"); private static final Pattern multipleSlashPattern = Pattern.compile("//+"); private static final Pattern initialParentPattern = Pattern.compile("/?(\\.\\./)*"); private static final Pattern embeddedParentPattern = Pattern.compile("[^/]+/\\.\\./"); private static final Pattern trailingParentPattern = Pattern.compile("[^/]+/\\.\\.$"); private static final Pattern initialAbsParentPattern = Pattern.compile("^/(\\.\\./)+"); private static final Pattern initialAbsSingleParentPattern = Pattern.compile("^/\\.\\.$"); private ResourceUtils() {} /** * Checks if the URI points to the local file. * @param uri the uri to check * @return true if the URI points to a local file */ public static boolean isLocalFile(URI uri) { if (!uri.isAbsolute()) return false; if (uri.isOpaque()) return false; if (!"file".equalsIgnoreCase(uri.getScheme())) return false; if (uri.getAuthority() != null) return false; if (uri.getFragment() != null) return false; if (uri.getQuery() != null) return false; if ("".equals(uri.getPath())) return false; return true; } /** * Returns the path converted to the canonic form. Examples: * <pre> * "/aaa/b/" -> "/aaa/b/" * "/aaa/b/c/../.." -> "/aaa/" * "/aaa/../bbb/cc/./.././../dd/eee/fff/." -> "/dd/eee/fff/" * "../aaa/../././bbb/./../ccc/" -> "../ccc/" * "aa/ddfdd/./sadfd/.././sdafa/../../.././././" -> "" * "./aaa/." -> "aaa/" * ".///aa//bb/" -> "aa/bb/" * "../../aaa" -> "../../aaa" * "/../../aaa" -> "/aaa" * </pre> */ public static String canonizePath(String path) { StringBuffer buf = new StringBuffer(path); StringBuffer aux = new StringBuffer(); while (replaceAll(buf, aux, dotInMiddlePattern, "/", 0)); replaceAll(buf, aux, multipleSlashPattern, "/", 0); replaceFirst(buf, aux, dotAtBegPattern, "", 0); replaceFirst(buf, aux, dotAtEndPattern, "/", 0); int pos = 0; while (pos < buf.length()) { pos += skipPrefix(buf, aux, initialParentPattern, pos); if (!replaceFirst(buf, aux, embeddedParentPattern, "", pos)) { break; } } replaceFirst(buf, aux, trailingParentPattern, "", pos); replaceFirst(buf, aux, initialAbsParentPattern, "/", 0); replaceFirst(buf, aux, initialAbsSingleParentPattern, "/", 0); return buf.toString(); } public static boolean isAbsolute(String path) { return (path.length() > 0 && path.charAt(0) == '/'); } private static boolean replaceFirst(StringBuffer buf, StringBuffer aux, Pattern pattern, String replacement, int pos) { boolean chg = false; aux.setLength(0); Matcher matcher = pattern.matcher(buf); if (matcher.find(pos)) { matcher.appendReplacement(aux, replacement); chg = true; } matcher.appendTail(aux); buf.setLength(0); buf.append(aux); return chg; } private static boolean replaceAll(StringBuffer buf, StringBuffer aux, Pattern pattern, String replacement, int pos) { aux.setLength(0); Matcher matcher = pattern.matcher(buf); boolean found = matcher.find(pos); boolean chg = found; while (found) { matcher.appendReplacement(aux, replacement); found = matcher.find(); } matcher.appendTail(aux); buf.setLength(0); buf.append(aux); return chg; } private static int skipPrefix(StringBuffer buf, StringBuffer aux, Pattern pattern, int pos) { Matcher matcher = initialParentPattern.matcher(buf); if (matcher.find(pos)) { return matcher.end()-pos; } return 0; } }