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.
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;
}
David
August 31, 2009, August 31, 2009 23:08, permalink
i would like to use the second method, but the problem is, i absolutely have no idea what the class 'DataUtil' should be... your own implementation??
would be nice if you could email me, thx!
Andy Roberts
January 21, 2010, January 21, 2010 15:42, permalink
There's some code on StackOverflow which makes this a doddle. See http://stackoverflow.com/questions/1399126/java-util-zip-recreating-directory-structure. The below code will emit the string "path/myfile.txt".
File mydir = new File("C:\\mydir");
File myfile = new File("C:\\mydir\\path\\myfile.txt");
System.out.println(mydir.toURI().relativize(myfile.toURI()).getPath());
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.