Monday, July 6, 2009

How to create valid file name?

During development of OOo2GD I met some non-trivial problem.
In Google Docs you may use almost all characters in title of document, but you cannot use all those characters in file name. This means that when user wants to download document to local machine you need sometimes to convert title to file name.... And of course we want to make sure that file name will be as much similar to document title as it is possible.

I thought about two possible ways, first by creating smallest common caharset for all operating systems [or rather file systems]. Second by narrowing available charset in steps.
I choose 2nd way, and decided that first I will try to remove only ":", if it will not help I will try to remove some bigger set of characters, and if it will not help too I will keep only latin letters and digits.

Here is code which I'm using :-)
public static String findAvailableFileName(String destFileURI) {
String destFileName = destFileURI.substring(0,destFileURI.lastIndexOf("."));
String destFileExt = destFileURI.substring(destFileURI.lastIndexOf(".")+1);
int count = 1;
File f;
while ((f=new File(destFileURI)).exists()) {
destFileURI=destFileName+"("+(count++)+")"+"."+destFileExt;
}
String fName = f.getName();
String fPath = f.getParent();
// Now we need to check if given file name is valid for file system, and if it isn't we need to convert it to valid form
if (!(testIfFileNameIsValid(destFileURI))) {
List forbiddenCharsPatterns = new ArrayList();
forbiddenCharsPatterns.add("[:]+"); // Mac OS, but it looks that also Windows XP
forbiddenCharsPatterns.add("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+"); // Windows
forbiddenCharsPatterns.add("[^\\w\\d\\.]+"); // last chance... only latin letters and digits
for (String pattern:forbiddenCharsPatterns) {
String nameToTest = fName;
nameToTest = nameToTest.replaceAll(pattern, "_");
destFileURI=fPath+"/"+nameToTest;
count=1;
destFileName = destFileURI.substring(0,destFileURI.lastIndexOf("."));
destFileExt = destFileURI.substring(destFileURI.lastIndexOf(".")+1);
while ((f=new File(destFileURI)).exists()) {
destFileURI=destFileName+"("+(count++)+")"+"."+destFileExt;
}
if (testIfFileNameIsValid(destFileURI)) break;
}
}
return destFileURI;
}

private static boolean testIfFileNameIsValid(String destFileURI) {
boolean valid = false;
try {
File candidate = new File(destFileURI);
String canonicalPath = candidate.getCanonicalPath();
boolean b = candidate.createNewFile();
if (b) {
candidate.delete();
}
valid = true;
} catch (IOException ioEx) { }
return valid;
}

Here nicer form of sources ;-)

Because whole operation in code above is performed on file path in first step I remove all / and \...

Additionally this code returns "first free" file name. So if you are trying to save file "test.odt" to directory where file with this name exists, this code will test first if "test.odt" is available, if not it will try "test(1).odt", next "test(2).odt" and so on.

Feel free to comment :-)


Similar postsbeta
Recursion is evil ;-)
How many i 1+1 in Java? ;-)
The Secret Life of String ;-)
How we may use Google Earth Plugin? :-)
Abuse of Booleans ;-)

3 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. I think, if a given filename already exist, don't need to revalidate filename...

    My (working :) version is:

    public static String findAvailableFileName(String fileDir, String fileName, String fileExt) {
    String resFileName=getCountedFileName(fileDir,fileName,fileExt);
    if (resFileName!=null) {
    return resFileName;
    }
    String destFileURI=fileDir+fileName+"."+fileExt;
    // Now we need to check if given file name is valid for file system, and if it isn't we need to convert it to valid form
    if (!(testIfFileNameIsValid(destFileURI))) {
    ArrayList forbiddenCharsPatterns = new ArrayList();
    forbiddenCharsPatterns.add("[:]+"); // Mac OS, but it looks that also Windows XP
    forbiddenCharsPatterns.add("[\\*\"/\\\\\\[\\]\\:\\;\\|\\=\\,]+"); // Windows
    forbiddenCharsPatterns.add("[^\\w\\d\\.]+"); // last chance... only latin letters and digits
    for (String pattern:forbiddenCharsPatterns) {
    String nameToTest = fileName;
    nameToTest = nameToTest.replaceAll(pattern, "_");
    destFileURI=fileDir+nameToTest;
    resFileName = getCountedFileName(fileDir, nameToTest, fileExt);
    if (resFileName!=null) {
    return resFileName;
    } else if (testIfFileNameIsValid(destFileURI)){
    return resFileName;
    }
    }
    } else {
    resFileName=fileName+"."+fileExt;
    return resFileName;
    }

    fileName="csjob_noname";
    resFileName=getCountedFileName(fileDir,fileName,fileExt);
    return resFileName;
    }

    /**
    * @param fileDir
    * @param fileName
    * @param ext
    * @return counted filename, if file exist. If file doesnt exist, return null
    */
    private static String getCountedFileName(String fileDir, String fileName, String fileExt) {
    boolean fileExist=false;
    int count = 1;
    String destFileURI=fileDir+fileName+fileExt;
    while ((new File(destFileURI)).exists()) {
    destFileURI=fileDir + fileName+"("+(count++)+")"+"."+fileExt;
    fileExist=true;
    }
    if (fileExist) {
    return fileName+"("+(count++)+")"+"."+fileExt; //filename valid and need incrementation...
    } else {
    return null; //file name doesnt exits
    }

    }


    private static boolean testIfFileNameIsValid(String destFileURI) {
    boolean valid = false;
    try {
    File candidate = new File(destFileURI);
    boolean b = candidate.createNewFile();
    if (b) {
    candidate.delete();
    }
    valid = true;
    } catch (IOException ioEx) { }
    return valid;
    }

    ReplyDelete