Analyzing file pathnames

exercise No. 142

Q:

Regarding relative and absolute path specifications you may want to read the first three paragraphs of section File Systems and Paths in chapter 16 of [Kurniawan].

Depending on your operating system of choice absolute (=fully qualified) filenames are being represented differently:

Windows:

c:\Users\student\Desktop\Downloads\avira.v3.zip

Such path names may be decomposed into:

Drive C:
Path \Users\student\Desktop\Downloads
file basename avira.v3
file extension zip
UNIX-like (Mac-OS, Linux,...):

/usr/share/gpa/gpa-logo.ppm

Possible decomposition:

Drive - (not applicable)
Path /usr/share
file basename gpagpa-logo
file extension ppm

Implement a class FileMetaInfo to be instantiated from ordinary strings describing absolute or relative filenames. Your class shall have several immutable attributes corresponding to the previously described values. Consider the following usage example:

public class WindowsExample {

    /**
     * @param args Unused
     */
    public static void main( String[] args ) {
       final FileMetaInfo fmi =
              new FileMetaInfo("C:\\users\\heinz\\Desktop\\index.html");

       System.out.println("Filename is relative? " + fmi.isRelative);
       System.out.println("Drive letter:" + fmi.drive);
       System.out.println("Directory path: " + fmi.path);
       System.out.println("File basename: " + fmi.basename);
       System.out.println("File extension: " + fmi.extension);
    }
}

On a Windows system the following output is being expected:

Filename is relative? false
Drive letter:C
Directory path: \users\heinz\Desktop
File basename: index
File extension: html

Due to the possible presence of drive letters Windows pathnames are more difficult to parse. We illustrate a possible strategy dissecting C:\programms\openvpn\conf\hdm.ovpn into the desired constituents:

Decomposing path components:

C:\programms\openvpn\conf\hdm.ovpn → {"C:", "programms", "openvpn", "conf", "hdm.ovpn"}.

Drive letter string to character normalization

"C:" → 'C'

Decomposing filename (if required)

"hdm.ovpn" → {"hdm", "ovpn"}

Hints:

  1. You will have to deal with absolute and relative (e.g. ../../Desktop/var.png) filename specifications.

  2. You will have to deal with files having no extensions like e.g. /usr/bin/firefox. In this case the extension attribute is expected to be null rather than "".

  3. For the sake of limiting complexity do not consider filenames starting with a period (like /home/heinz/.bashrc) or containing multiple periods like ../arrow.right.png.

  4. To distinguish the above mentioned system defaults for filename path conventions read the java.lang.System section of chapter 5 along with its API. The table of system properties in [Kurniawan] will enable your implementation to work both on Windows and non-Windows systems accordingly.

  5. You will have to parse filenames like "/usr/share/openvpn/hdm.conf". Exercise 7 from the Quiz section of chapter 5 in [Kurniawan] shows an example dissecting a string containing tokens being separated by space characters using the java.util.StringTokenizer class. Read its documentation and learn how to specify string delimiters other than default space ' '. It'll allow you to dissect paths using separators '/' (Unix) or '\' (Windows) to break paths into components.

    Likewise you may split filenames into basename / extension by using '.' as separator.

  6. Assembling path components may be effected by either using a StringBuffer or a StringJoiner instance. Mind the distinction between relative and absolute paths!

  7. The methods indexOf(...) and substring(...) come in handy when dissecting filenames into basename and extension.

  8. The backslash \ must be escaped when being part of a Java String literal:

       String path = "C:\\programms\\openvpn\\conf\\hdm.ovpn";
  9. Unit tests may be written for Windows and non-Windows systems separately. Read https://www.thekua.com/atwork/2008/09/running-tests-on-a-specific-os-under-junit4 how to use Assume.assumeFalse() / Assume.assumeTrue(...) to separate Windows from Unix tests.

  10. Test your implementation both on Windows and Unix-type platforms. The following tests may be used as a starting point:

    public class MetaInfoUnixTest {
    
       static private final boolean isWindows =
                 System.getProperty("os.name").startsWith("Windows");
    
       @Test
       public void testAbsoluteWithoutExtension() {
           Assume.assumeFalse(isWindows);
    
           final FileMetaInfo fmi = new FileMetaInfo("/usr/openvpn/hdm");
    
           Assert.assertFalse(fmi.isRelative);
           Assert.assertEquals(FileMetaInfo.noDrive, fmi.drive);
           Assert.assertEquals("/usr/openvpn", fmi.path);
           Assert.assertEquals("hdm", fmi.basename);
           Assert.assertEquals(null, fmi.extension);
       }
    
       @Test
       public void testAbsoluteWithExtension() {
           Assume.assumeFalse(isWindows);
    
           final FileMetaInfo fmi = new FileMetaInfo("/usr/openvpn/hdm.conf");
    
           Assert.assertFalse(fmi.isRelative);
           Assert.assertEquals(FileMetaInfo.noDrive, fmi.drive);
           Assert.assertEquals("/usr/openvpn", fmi.path);
           Assert.assertEquals("hdm", fmi.basename);
           Assert.assertEquals("conf", fmi.extension);
       }
    
       @Test
       public void testRelativeWithoutExtension() {
           Assume.assumeFalse(isWindows);
    
           final FileMetaInfo fmi = new FileMetaInfo("../Desktop/icon");
    
           Assert.assertTrue(fmi.isRelative);
           Assert.assertEquals(FileMetaInfo.noDrive, fmi.drive);
           Assert.assertEquals("../Desktop", fmi.path);
           Assert.assertEquals("icon", fmi.basename);
           Assert.assertEquals(null, fmi.extension);
       }
    
       @Test
       public void testRelativeWithExtension() {
           Assume.assumeFalse(isWindows);
    
           final FileMetaInfo fmi = new FileMetaInfo("../../Desktop/icon.gif");
    
           Assert.assertTrue(fmi.isRelative);
           Assert.assertEquals(FileMetaInfo.noDrive, fmi.drive);
           Assert.assertEquals("../../Desktop", fmi.path);
           Assert.assertEquals("icon", fmi.basename);
           Assert.assertEquals("gif", fmi.extension);
       }
    }
    public class MetaInfoWindowsTest {
    
       static private final boolean isWindows =
                        System.getProperty("os.name").startsWith("Windows");
    
       @Test
       public void testAbsoluteWithoutExtension() {
         Assume.assumeTrue(isWindows);
    
         final FileMetaInfo fmi =
              new FileMetaInfo("F:\\software\\my\\archive");
    
         Assert.assertFalse(fmi.isRelative);
         Assert.assertEquals('F', fmi.drive);
         Assert.assertEquals("\\software\\my", fmi.path);
         Assert.assertEquals("archive", fmi.basename);
         Assert.assertEquals(null, fmi.extension);
       }
    
       @Test
       public void testAbsoluteWithExtension() {
           Assume.assumeTrue(isWindows);
    
           final FileMetaInfo fmi =
               new FileMetaInfo("F:\\subdir\\archive.zip");
    
           Assert.assertFalse(fmi.isRelative);
           Assert.assertEquals('F', fmi.drive);
           Assert.assertEquals("\\subdir", fmi.path);
           Assert.assertEquals("archive", fmi.basename);
           Assert.assertEquals("zip", fmi.extension);
       }
    
       @Test
       public void testRelativeWithoutExtension() {
           Assume.assumeTrue(isWindows);
    
           final FileMetaInfo fmi = new FileMetaInfo("..\\Desktop\\icon");
    
           Assert.assertTrue(fmi.isRelative);
           Assert.assertEquals(FileMetaInfo.noDrive, fmi.drive);
           Assert.assertEquals("..\\Desktop", fmi.path);
           Assert.assertEquals("icon", fmi.basename);
           Assert.assertEquals(null, fmi.extension);
       }
    
       @Test
       public void testRelativeWithExtension() {
           Assume.assumeTrue(isWindows);
    
           final FileMetaInfo fmi =
               new FileMetaInfo("..\\..\\Desktop\\icon.gif");
    
           Assert.assertTrue(fmi.isRelative);
           Assert.assertEquals(FileMetaInfo.noDrive, fmi.drive);
           Assert.assertEquals("..\\..\\Desktop", fmi.path);
           Assert.assertEquals("icon", fmi.basename);
           Assert.assertEquals("gif", fmi.extension);
       }
    }

A: