2
\$\begingroup\$

I am writing wrapper classes in Java which override methods of an existing implementation, in order to handle an edge case. The full implementation is a bit more complex than needs to be posted here, so I've written a simplified class containing only the parts I'm requesting assistance on.

Problem Summary

I am extending two classes:

One class designed as an "enumeration" class, abstracting a directory on a filesystem which contains symbolic links to other directories. (Real world: "/sys/block".). It has two methods, a scan() method to generate the list of (linked) subdirectories, and a getFirst() to return the first element of the list.

The second class is an "entry" class, abstracting the pointed-to directory enumerated by the first class. It has two methods, a getName() method to return the directory's path as a string, and a getNext() method to iterate to the next element.

Constraints

  • Compatibility with JDK 8 or earlier
  • Single-threaded use may be assumed
  • Constructors may be altered as required.
  • Must implement (at least) the two specified classes and the two methods on each.

Focus of review

The scan() method is my struggle here. I think I may have overcomplicated the solution in two ways:

  • The nested try ... catch blocks in the scan() method seem unusual. Am I missing a simpler way to handle this?
  • (UPDATE: Self-answered this second question, below.) The implemented pattern is obviously a singly linked list that I'm working around by passing around an ArrayList implementation. I can imagine the DirEntry class containing only its Path and a DirEntry next object, but attempts to generate such a list seem even more complex or less performant than the workaround I've created.
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DeviceList {

    /**
     * Class representing a parent directory which contains symbolic links to other
     * directories
     */
    static class DirEnumerator {

        private Path dirPath;
        private List<DirEntry> entryList = Collections.emptyList();

        public DirEnumerator(String path) {
            dirPath = FileSystems.getDefault().getPath(path);
        }

        /**
         * Scans the directory for entries
         *
         * @return The number of entries found
         */
        public int scan() {
            try (Stream<Path> paths = Files.walk(dirPath)) {
                List<Path> linkedDirs = paths.filter(Files::isSymbolicLink).map(p -> {
                    try {
                        return Files.readSymbolicLink(p);
                    } catch (IOException e) {
                        return p;
                    }
                }).collect(Collectors.toList());
                this.entryList = new ArrayList<>();
                for (int i = 0; i < linkedDirs.size(); i++) {
                    this.entryList.add(new DirEntry(entryList, linkedDirs.get(i), i));
                }
                return this.entryList.size();
            } catch (IOException e) {
                this.entryList = Collections.emptyList();
                return 0;
            }
        }

        /**
         * Gets the first entry in the scanned list
         *
         * @return The first entry if it exists; null otherwise
         */
        public DirEntry getFirst() {
            return entryList.isEmpty() ? null : entryList.get(0);
        }

    }

    /**
     * Class representing a directory
     */
    static class DirEntry {
        private List<DirEntry> entryList;
        private Path path;
        private int index;

        public DirEntry(List<DirEntry> entryList, Path path, int i) {
            this.entryList = entryList;
            this.path = path;
            this.index = i;
        }

        /**
         * Gets the path name of the directory entry
         *
         * @return a string representing the path
         */
        public String getName() {
            return this.path.toString();
        }

        /**
         * Gets the next entry in the list
         *
         * @return the next entry if it exists; null otherwise
         */
        public DirEntry getNext() {
            int nextIndex = index + 1;
            return nextIndex < entryList.size() ? entryList.get(nextIndex) : null;
        }
    }

    public static void main(String[] args) {
        // Test on any directory containing symbolic links to other directories
        DirEnumerator de = new DirEnumerator("/sys/block");
        int n = de.scan();
        System.out.println("Found " + n + " directories.");

        DirEntry e = de.getFirst();
        while (e != null) {
            System.out.println("Directory: " + e.getName());
            e = e.getNext();
        }
    }
}
```
\$\endgroup\$
2
  • \$\begingroup\$ It seems like the getNext method should be a method of the Enumeration class, not the Entry class. \$\endgroup\$ – Gilbert Le Blanc Aug 15 '20 at 16:59
  • \$\begingroup\$ @GilbertLeBlanc I agree, but unfortunately those are the constraints I'm working under for the class I'm extending. I think I did figure out a way to do that list portion and may self-answer the second question momentarily. First question still valid. \$\endgroup\$ – Daniel Widdis Aug 15 '20 at 17:21
0
\$\begingroup\$

I have figured out a simpler way to do the second question, building the Linked List by iterating backwards from the generated paths.

    static class DirEnumerator {

        private Path dirPath;
        private DirEntry first = null;

        // ...

        public int scan() {
            try (Stream<Path> paths = Files.walk(dirPath)) {
                List<Path> linkedDirs = paths.filter(Files::isSymbolicLink).map(p -> {
                    try {
                        return Files.readSymbolicLink(p);
                    } catch (IOException e) {
                        return p;
                    }
                }).collect(Collectors.toList());
                this.first = null;
                int i = linkedDirs.size();
                while (i-- > 0) {
                    this.first = new DirEntry(linkedDirs.get(i), first);
                }
                return linkedDirs.size();
            } catch (IOException e) {
                this.first = null;
                return 0;
            }
        }

        // ...
    }

    static class DirEntry {
        private Path path;
        private DirEntry next;

        public DirEntry(Path path, DirEntry next) {
            this.path = path;
            this.next = next;
        }

       // ...
    }
```
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.