1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
public static String getRelativePath(File file, File relativeTo) throws IOException { /* * windows seems in some cases not to stop getParent() at 'c:\', which I * considered to be root. For that reason I had to tweak in the * following to 'ugly' lines: */ file = new File(file + File.separator + "89243jmsjigs45u9w43545lkhj7").getParentFile(); relativeTo = new File(relativeTo + File.separator + "984mvcxbsfgqoykj30487df556").getParentFile(); File origFile = file; File origRelativeTo = relativeTo; ArrayList<File> filePathStack = new ArrayList<File>(); ArrayList<File> relativeToPathStack = new ArrayList<File>(); // build the path stack info to compare it afterwards file = file.getCanonicalFile(); while (file != null) { filePathStack.add(0, file); file = file.getParentFile(); } relativeTo = relativeTo.getCanonicalFile(); while (relativeTo != null) { relativeToPathStack.add(0, relativeTo); relativeTo = relativeTo.getParentFile(); } // compare as long it goes int count = 0; file = filePathStack.get(count); relativeTo = relativeToPathStack.get(count); while ((count < filePathStack.size() - 1) && (count < relativeToPathStack.size() - 1) && file.equals(relativeTo)) { count++; file = filePathStack.get(count); relativeTo = relativeToPathStack.get(count); } if (file.equals(relativeTo)) count++; // up as far as necessary StringBuffer relString = new StringBuffer(); for (int i = count; i < relativeToPathStack.size(); i++) { relString.append(".." + File.separator); } // now back down to the file for (int i = count; i < filePathStack.size() - 1; i++) { relString.append(filePathStack.get(i).getName() + File.separator); } relString.append(filePathStack.get(filePathStack.size() - 1).getName()); // just to test File relFile = new File(origRelativeTo.getAbsolutePath() + File.separator + relString.toString()); if (!relFile.getCanonicalFile().equals(origFile.getCanonicalFile())) { throw new IOException("Failed to find relative path."); } return relString.toString(); }
Refactorings
No refactoring yet !
Mustafa
November 3, 2008, November 03, 2008 07:45, permalink
I cannot say that this is a better solution, but just another way with string manipulation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
public static String getRelativePath(File file, File relativeTo) { String path = ""; List<String> fileList = DataUtil.getDelimitedStringAsList(file.getAbsolutePath(),File.separator); List<String> relativeList = DataUtil.getDelimitedStringAsList(relativeTo.getAbsolutePath(),File.separator); int size = fileList.size(); int relativeSize = relativeList.size(); int count = 0; //ignore same parents while(count<size && count<relativeSize) { if(fileList.get(count).equals(relativeList.get(count))) count++; else break; } for (int i = count; i < relativeSize; i++) { path += ".." + File.separator; } for (int i = count; i < size; i++) { path += fileList.get(i) + File.separator; } if(path.indexOf(File.separator)>-1) path = path.substring(0,path.lastIndexOf(File.separator)); return path; } public static List getDelimitedStringAsList(String str, String delimiter) { List resultList = new ArrayList(); StringTokenizer st = new StringTokenizer(str, delimiter); while (st.hasMoreTokens()) resultList.add(st.nextToken()); return resultList; }
I call:
File f = new File("c:\\windows\\system32\\asd.tmp");
File r = new File("c:\\windows\\web");
getRelativePath(f,r) returns "..\\system32\\asd.tmp");
The author of this method put two ugly lines for solving a windows problem.
I tried this code and I think it works fine, but I'm not sure this is the best way to do it.
A function that finds the relative path of a file is often useful.